转自 程序员的自我修养 – SelfUp.cn

由于在学习 spark mllib 但是如此详细的资料真的很难找,在此分享。1,220次浏览

什么是协同过滤

协同过滤(Collaborative Filtering, 简称CF),wiki上的定义是:简单来说是利用某兴趣相投、拥有共同经验之群体的喜好来推荐使用者感兴趣的资讯,个人透过合作的机制给予资讯相当程度的回应(如评分)并记录下来以达到过滤的目的进而帮助别人筛选资讯,回应不一定局限于特别感兴趣的,特别不感兴趣资讯的纪录也相当重要。

以上定义太拗口,举个简单的例子:我现在多年不看日本anime的新番了,最近突然又想看几部新番,但又不知道这么多新番中看哪些比较好,于是我就找几个同样喜欢日本动漫的朋友来咨询。我第一个想咨询的朋友是和我口味最像的,我们都特别喜欢看《虫师》、《黑之契约者》、《寒蝉》这样的小众动画;我问的第二个朋友和我口味差不多,他特别喜欢看《钢炼》《无头骑士异闻录》这样的动画,我虽然喜欢,但不像他那么喜欢;由于身边喜欢日本动画的朋友不多,剩下第三个可以咨询的是一个宅女,平常经常看腐、宅、基的动漫,完全跟我不是一路人,于是问了下她推荐的片子,并将这些片子打上的黑名单的标签。然后我就开始看第一个朋友推荐的片子了,要是时间特别多又很无聊我可能会看第二个朋友推荐的,但打死我也不会看第三个朋友推荐的。这就是协同过滤的一个简化、小众版。

如何进行相似度度量

接着上面的例子,我可以通过我和其它朋友共同喜欢某个或某类动漫来确定我们的口味是否一样,那么如何以数学或者机器的形式来表示这个“口味一样”呢?通常,是通过“距离”来表示,例如:欧几里得距离、皮尔逊相关度、曼哈顿距离、Jaccard系数等等。

欧几里得距离

欧几里德距离(Euclidean Distance),最初用于计算欧几里得空间中两个点的距离,在二维空间中,就是我们熟悉的两点间的距离,x、y表示两点,维度为n:

d(x,y)=(∑ni(xi−yi)2)−−−−−−−−−−−−√

相似度:

sim(x,y)=11+d(x,y)

皮尔逊相关度

皮尔逊相关度(Pearson Correlation Coefficient),用于判断两组数据与某一直线拟合程度的一种度量,取值在[-1,1]之间。当数据不是很规范的时候(如偏差较大),皮尔逊相关度会给出较好的结果。

p(x,y)=∑xiyi−nxy¯¯¯¯¯¯(n−1)SxSy=n∑xiyi−∑xi∑yin∑x2i−(∑xi)2√n∑y2i−(∑yi)2√

曼哈顿距离

曼哈顿距离(Manhattan distance),就是在欧几里得空间的固定直角坐标系上两点所形成的线段对轴产生的投影的距离总和。

d(x,y)=∑∥xi−yi∥

Jaccard系数

Jaccard系数,也称为Tanimoto系数,是Cosine相似度的扩展,也多用于计算文档数据的相似度。通常应用于x为布尔向量,即各分量只取0或1的时候。此时,表示的是x,y的公共特征的占x,y所占有的特征的比例:

T(x,y)=x∙y∥x∥2+∥y∥2−x∙y=∑xiyi∑x2i√+∑y2i√−∑xiyi

计算推荐

根据上述“距离”的算法,我们可以找出与自己“口味一样”的人了,但这并不是目的,目的是找出推荐的物品。一种常用的做法是选出与你兴趣相同的N个人,然后根据这N个人的记录来进行加权推荐。具体如下,假设已经计算出欧几里得相似度:

朋友 相似度 银魂 S.x银魂 食灵零 S.x食灵零 雨月 S.x雨月
A 0.95 10.0 9.5 9.0 8.55 - -
B 0.80 8.5 6.8 7.5 6 5.0 4
C 0.25 - - 6.5 1.625 9.0 2.25
总计 16.3 16.175 6.25
Sim.Sum 1.75 2 1.05
总计/Sim.Sum 9.31 8.09 5.95

其中,S.x开头的表示相似度与评分的乘积,Sim.Sum表示打过分的朋友的相似度之和。可以看出根据三位友人的推荐,我从这三部动漫中应该选择银魂来看。

Item CF与User CF

基于用户的协同过滤(User CF),其基本思想相当简单,基于用户对物品的偏好找到相邻邻居用户,然后将邻居用户喜欢的推荐给当前用户。上述过程就属于User CF。

基于物品的CF(Item CF)的原理和基于用户的CF类似,只是在计算邻居时采用物品本身,而不是从用户的角度,即基于用户对物品的偏好找到相似的物品,然后根据用户的历史偏好,推荐相似的物品给他。

两者的计算复杂度和适用场景皆不同,详细可参见参考资料。

Spark协同过滤实现

训练数据

1
2
3
4
5
6
7

196      242      3      881250949
186      302      3      891717742
22      377      1      878887116
244      51      2      880606923
166      346      1      886397596
298      474      4      884182806
. . .

其中第一列为userid,第二列为movieid,第三列为评分,第四列为timestamp(未使用)。

完整版下载:MovieLens(解压后的u.data文件)。

代码实现

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66

public class UserSideCF implements Serializable {
     private static final Pattern TAB = Pattern . compile ( "\t" ) ;
     public MatrixFactorizationModel buildModel ( RDD < Rating > rdd ) { //训练模型
         int rank = 10 ;
         int numIterations = 20 ;
         MatrixFactorizationModel model = ALS . train ( rdd , rank , numIterations , 0.01 ) ;
         return model ;
     }
     public RDD < Rating > [ ] splitData ( ) { //分割数据,一部分用于训练,一部分用于测试
         SparkConf sparkConf = new SparkConf ( ) . setAppName ( "JavaALS" ) . setMaster ( "local[2]" ) ;
         JavaSparkContext sc = new JavaSparkContext ( sparkConf ) ;
         JavaRDD < String > lines = sc . textFile ( "/home/nodin/ml-100k/u.data" ) ;
         JavaRDD < Rating > ratings = lines . map ( line -> {
             String [ ] tok = TAB . split ( line ) ;
             int x = Integer . parseInt ( tok [ 0 ] ) ;
             int y = Integer . parseInt ( tok [ 1 ] ) ;
             double rating = Double . parseDouble ( tok [ 2 ] ) ;
             return new Rating ( x , y , rating ) ;
         } ) ;
         RDD < Rating > [ ] splits = ratings . rdd ( ) . randomSplit ( new double [ ] { 0.6 , 0.4 } , 11L ) ;
         return splits ;
     }
     public static void main ( String [ ] args ) {
         UserSideCF cf = new UserSideCF ( ) ;
         RDD < Rating > [ ] splits = cf . splitData ( ) ;
         MatrixFactorizationModel model = cf . buildModel ( splits [ 0 ] ) ;
         Double MSE = cf . getMSE ( splits [ 0 ] . toJavaRDD ( ) , model ) ;
         System . out . println ( "Mean Squared Error = " + MSE ) ; //训练数据的MSE
         Double MSE1 = cf . getMSE ( splits [ 1 ] . toJavaRDD ( ) , model ) ;
         System . out . println ( "Mean Squared Error1 = " + MSE1 ) ; //测试数据的MSE
     }
     public Double getMSE ( JavaRDD < Rating > ratings , MatrixFactorizationModel model ) { //计算MSE
         JavaPairRDD usersProducts = ratings . mapToPair ( rating -> new Tuple2 <> ( rating . user ( ) , rating . product ( ) ) ) ;
         JavaPairRDD < Tuple2 < Integer , Integer > , Double > predictions = model . predict ( usersProducts . rdd ( ) )
               . toJavaRDD ( )
               . mapToPair ( new PairFunction < Rating , Tuple2 < Integer , Integer > , Double > ( ) {
                   @ Override
                   public Tuple2 < Tuple2 < Integer , Integer > , Double > call ( Rating rating ) throws Exception {
                       return new Tuple2 <> ( new Tuple2 <> ( rating . user ( ) , rating . product ( ) ) , rating . rating ( ) ) ;
                   }
               } ) ;
         JavaPairRDD < Tuple2 < Integer , Integer > , Double > ratesAndPreds = ratings
               . mapToPair ( new PairFunction < Rating , Tuple2 < Integer , Integer > , Double > ( ) {
                   @ Override
                   public Tuple2 < Tuple2 < Integer , Integer > , Double > call ( Rating rating ) throws Exception {
                       return new Tuple2 <> ( new Tuple2 <> ( rating . user ( ) , rating . product ( ) ) , rating . rating ( ) ) ;
                   }
               } ) ;
         JavaPairRDD joins = ratesAndPreds . join ( predictions ) ;
         return joins . mapToDouble ( new DoubleFunction < Tuple2 < Tuple2 < Integer , Integer > , Tuple2 < Double , Double >>> ( ) {
             @ Override
             public double call ( Tuple2 < Tuple2 < Integer , Integer > , Tuple2 < Double , Double >> o ) throws Exception {
                 double err = o . _2 ( ) . _1 ( ) - o . _2 ( ) . _2 ( ) ;
                 return err * err ;
             }
         } ) . mean ( ) ;
     }
}

运行结果

1
2
3
4

0.6 , 0.4(训练数据,测试数据) - 0.3706799281981904 , 2.4569381099423957(训练数据,测试数据)
0.7 , 0.3 - 0.40358067085112936 , 2.4469113734667935
0.8 , 0.2 - 0.4335027003381571 , 2.0930908173274476
0.9 , 0.1 - 0.4587619714761296 , 1.7213014771993198

参考资料

  • MLlib - Collaborative Filtering
  • 探索推荐引擎内部的秘密,第 2 部分: 深入推荐引擎相关算法 - 协同过滤
  • Movie Recommendation with MLlib

孙其功陪你学之——Spark MLlib之协同过滤相关推荐

  1. 使用spark mllib中协同过滤推荐算法ALS建立推荐模型

    使用spark mllib中协同过滤推荐算法ALS建立推荐模型 package com.yyds.tags.ml.rs.rddimport org.apache.spark.mllib.evaluat ...

  2. Spark MLlib之协同过滤

    原文:http://blog.selfup.cn/1001.html 什么是协同过滤 协同过滤(Collaborative Filtering, 简称CF),wiki上的定义是:简单来说是利用某兴趣相 ...

  3. 孙其功陪你学之——OJB文件介绍

    OBJ文件: OBJ文件是Alias|Wavefront公司为它的一套基于工作站的3D建模和动画软件"Advanced Visualizer"开发的一种标准3D模型文件格式,很适合 ...

  4. c语言读取.ppm文件,孙其功陪你学之——C语言读取.ppm文件

    C语言读取.ppm图像文件,读取的是每一个像素点的R.G.B的之0--255之间 在写读取程序时遇到好多问题,比如类型转换问题最是头疼,数据量特别大,开始读出的数据 可能有好大一部分为0,导致误认为读 ...

  5. Spark机器学习之协同过滤算法

    Spark机器学习之协同过滤算法 一).协同过滤 1.1 概念 协同过滤是一种借助"集体计算"的途径.它利用大量已有的用户偏好来估计用户对其未接触过的物品的喜好程度.其内在思想是相 ...

  6. 项目体系架构设计——基于Spark平台的协同过滤实时电影推荐系统项目系列博客(四)

    系列文章目录 初识推荐系统--基于Spark平台的协同过滤实时电影推荐系统项目系列博客(一) 利用用户行为数据--基于Spark平台的协同过滤实时电影推荐系统项目系列博客(二) 项目主要效果展示--基 ...

  7. 利用用户行为数据——基于Spark平台的协同过滤实时电影推荐系统项目系列博客(二)

    系列文章目录 初识推荐系统--基于Spark平台的协同过滤实时电影推荐系统项目系列博客(一) 利用用户行为数据--基于Spark平台的协同过滤实时电影推荐系统项目系列博客(二) 项目主要效果展示--基 ...

  8. 基础环境搭建——基于Spark平台的协同过滤实时电影推荐系统项目系列博客(五)

    系列文章目录 初识推荐系统--基于Spark平台的协同过滤实时电影推荐系统项目系列博客(一) 利用用户行为数据--基于Spark平台的协同过滤实时电影推荐系统项目系列博客(二) 项目主要效果展示--基 ...

  9. 实时推荐服务建设——基于Spark平台的协同过滤实时电影推荐系统项目系列博客(八)

    系列文章目录 初识推荐系统--基于Spark平台的协同过滤实时电影推荐系统项目系列博客(一) 利用用户行为数据--基于Spark平台的协同过滤实时电影推荐系统项目系列博客(二) 项目主要效果展示--基 ...

最新文章

  1. ACMNO.42 C语言-第几天 定义一个结构体变量(包括年、月、日)。计算该日在本年中是第几天,注意闰年问题。利用结构体的在最下面
  2. .Net 2.0 事件机制,更简化了...
  3. 2019寒假作业二:PTA7-1币值转换
  4. Java 获取当前时间
  5. 王义成:阿里云Redis服务助力游戏行业发展
  6. 26 | 红黑树(下):掌握这些技巧,你也可以实现一个红黑树
  7. AWS AI 全面助力视频理解,GluonCV 0.6 轻松复现前沿模型
  8. 00038oracle,ORACLE错误一览表
  9. 揭秘支撑双 11 买买买背后的硬核黑科技!
  10. avalon 笔记---Mr.wing
  11. C++:STL之vector,deque对比
  12. 编码基本功:给刚刚学习编程的朋友的建议
  13. python+selenium常见坑
  14. 使用Mediacoder压制带有图片的ass字幕
  15. 【Android -- 面试】简历模板
  16. 震撼您心灵的四川雪山
  17. Cannot find entry in either framework or device manifest
  18. 电阻器阻值的标称方法介绍
  19. LinQ的初步学习与总结
  20. 怎么申请电子邮箱账号?单位电子邮箱怎么申请?

热门文章

  1. oracle中怎么判断全为空格_生活中常见的哪些蔬菜草酸低?怎么判断呢
  2. toad for oracle win10,Install Toad for Oracle 10.6 on Winows 7 X64
  3. 学习笔记4--高精度地图关键技术(下)
  4. App如何更新数据(转)
  5. 【iMessage苹果相册推】 内部网关协议(InternetwayProtocol)常用的是RIP和OSPE
  6. IOS逆向学习-签名机制
  7. 单相在线式不间断电源(B 题)--2020 年TI 杯大学生电子设计竞赛
  8. SpringBoot ~ 邮件发送
  9. Windows sonarqube启动时卡在es上
  10. 微信小程序bug小全 : [“usingComponents“][“XXX“]: “../../components/XXX/XXX“ 未找到