机器学习(九) 协同过滤算法

协同过滤

【版权声明】本文为原创,转载请注明原地址 https://www.jianshu.com/p/865b1842fc0b
同步更新在个人网站:http://www.wangpengcufe.com/machinelearning/ml-ml9/

一、概念

协同过滤算法主要分为基于用户的协同过滤算法和基于项目的协同过滤算法。

基于用户的协同过滤算法和基于项目的协同过滤算法
1.1、以用户为基础(User-based)的协同过滤

用相似统计的方法得到具有相似爱好或者兴趣的相邻用户,所以称之为以用户为基础(User-based)的协同过滤或基于邻居的协同过滤(Neighbor-based Collaborative Filtering)。

具体步骤为:

1.收集用户信息
收集可以代表用户兴趣的信息。一般的网站系统使用评分的方式或是给予评价,这种方式被称为“主动评分”。另外一种是“被动评分”,是根据用户的行为模式由系统代替用户完成评价,不需要用户直接打分或输入评价数据。电子商务网站在被动评分的数据获取上有其优势,用户购买的商品记录是相当有用的数据。

2.最近邻搜索(Nearest neighbor search, NNS)
以用户为基础(User-based)的协同过滤的出发点是与用户兴趣爱好相同的另一组用户,就是计算两个用户的相似度。例如:查找n个和A有相似兴趣用户,把他们对M的评分作为A对M的评分预测。一般会根据数据的不同选择不同的算法,较多使用的相似度算法有Pearson Correlation Coefficient、Cosine-based Similarity、Adjusted Cosine Similarity。

3.产生推荐结果
有了最近邻集合,就可以对目标用户的兴趣进行预测,产生推荐结果。依据推荐目的的不同进行不同形式的推荐,较常见的推荐结果有Top-N 推荐和关系推荐。Top-N 推荐是针对个体用户产生,对每个人产生不一样的结果,例如:通过对A用户的最近邻用户进行统计,选择出现频率高且在A用户的评分项目中不存在的,作为推荐结果。关系推荐是对最近邻用户的记录进行关系规则(association rules)挖掘。

1.2、以项目为基础(Item-based)的协同过滤

以用户为基础的协同推荐算法随着用户数量的增多,计算的时间就会变长,所以在2001年Sarwar提出了基于项目的协同过滤推荐算法(Item-based Collaborative Filtering Algorithms)。以项目为基础的协同过滤方法有一个基本的假设“能够引起用户兴趣的项目,必定与其之前评分高的项目相似”,通过计算项目之间的相似性来代替用户之间的相似性。

具体步骤为:

1.收集用户信息
同以用户为基础(User-based)的协同过滤。

2.针对项目的最近邻搜索
先计算已评价项目和待预测项目的相似度,并以相似度作为权重,加权各已评价项目的分数,得到待预测项目的预测值。例如:要对项目 A 和项目 B 进行相似性计算,要先找出同时对 A 和 B 打过分的组合,对这些组合进行相似度计算,常用的算法同以用户为基础(User-based)的协同过滤。

3.产生推荐结果
以项目为基础的协同过滤不用考虑用户间的差别,所以精度比较差。但是却不需要用户的历史数据,或是进行用户识别。对于项目来讲,它们之间的相似性要稳定很多,因此可以离线完成工作量最大的相似性计算步骤,从而降低了在线计算量,提高推荐效率,尤其是在用户多于项目的情形下尤为显著。

二、隐式反馈 VS 显性反馈

2.1、概念

隐性反馈行为:不能明确反映用户喜好的行为。

显性反馈行为:用户明确表示对物品喜好的行为。

显性反馈行为包括用户明确表示对物品喜好的行为,隐性反馈行为指的是那些不能明确反应用户喜好的行为。在许多的现实生活中的很多场景中,我们常常只能接触到隐性的反馈,例如页面游览,点击,购买,喜欢,分享等等。
基于矩阵分解的协同过滤的标准方法,一般将用户商品矩阵中的元素作为用户对商品的显性偏好。在 MLlib 中所用到的处理这种数据的方法来源于文献: Collaborative Filtering for Implicit Feedback Datasets 。 本质上,这个方法将数据作为二元偏好值和偏好强度的一个结合,而不是对评分矩阵直接进行建模。因此,评价就不是与用户对商品的显性评分,而是与所观察到的用户偏好强度关联起来。然后,这个模型将尝试找到隐语义因子来预估一个用户对一个商品的偏好。

2.2、显性反馈数据和隐形反馈数据的比较
显性反馈数据和隐形反馈数据的比较
2.3、各代表网站中显性反馈数据和隐性反馈数据的例子
各代表网站中显性反馈数据和隐性反馈数据的例子

三、代码实现

下面代码读取spark的示例文件,文件中每一行包括一个用户id、商品id和评分。我们使用默认的ALS.train() 方法来构建推荐模型并评估模型的均方差。

3.1、导入需要的包:
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaPairRDD;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.mllib.recommendation.ALS;
import org.apache.spark.mllib.recommendation.MatrixFactorizationModel;
import org.apache.spark.mllib.recommendation.Rating;
3.2、读取数据:

首先,读取文本文件,把数据转化成rating类型,即[Int, Int, Double]的RDD;

/**
  * 1、获取spark
*/
SparkConf conf = new  SparkConf().setAppName("CollaborativeModel").setMaster("local");
JavaSparkContext sc = new  JavaSparkContext(conf);
/**
* 2、读取文本文件,把数据转化成rating类型,即[Int, Int, Double]的RDD
*/
JavaRDD<String> source =  sc.textFile("data/mllib/collaborative.data");
JavaRDD<Rating> ratings =  source.map(line->{
    String[] parts = line.split(",");
    return new  Rating(Integer.parseInt(parts[0]),
            Integer.parseInt(parts[1]),
            Double.parseDouble(parts[2]));
});
ratings.foreach(x->{
    System.out.println(x);
});
/**
*控制台输出结果:
*Rating中的第一个int是user编号,第二个int是item编号,最后的double是user对item的评分。
----------------
Rating(1,1,5.0)
Rating(3,2,5.0)
Rating(1,2,1.0)
Rating(3,3,1.0)
Rating(1,3,5.0)
Rating(3,4,5.0)
Rating(1,4,1.0)
Rating(4,1,1.0)
Rating(2,1,5.0)
Rating(4,2,5.0)
Rating(2,2,1.0)
Rating(4,3,1.0)
Rating(2,3,5.0)
Rating(4,4,5.0)
Rating(2,4,1.0)
Rating(3,1,1.0)
----------------
**/
3.3、构建模型

划分训练集和测试集,比例分别是0.8和0.2。

JavaRDD<Rating>[] splits =  ratings.randomSplit(new double[] {0.8,0.2});
JavaRDD<Rating> training = splits[0];
JavaRDD<Rating> test = splits[1];

指定参数值,然后使用ALS训练数据建立推荐模型:

int rank = 10;
int numIterations = 10;
/**
* 可以调整这些参数,不断优化结果,使均方差变小。比如:iterations越多,lambda较小,均方差会较小,推荐结果较优
*/
MatrixFactorizationModel model = ALS.train(training.rdd(),rank,numIterations,0.01);

在 MLlib 中的实现有如下的参数:

numBlocks 是用于并行化计算的分块个数 (设置为-1,为自动配置)。

rank 是模型中隐语义因子的个数。

iterations 是迭代的次数。

lambda 是ALS的正则化参数。

implicitPrefs 决定了是用显性反馈ALS的版本还是用适用隐性反馈数据集的版本。

alpha 是一个针对于隐性反馈 ALS 版本的参数,这个参数决定了偏好行为强度的基准。

可以调整这些参数,不断优化结果,使均方差变小。比如:iterations越多,lambda较小,均方差会较小,推荐结果较优。上面的例子中调用了 ALS.train(ratings, rank, numIterations, 0.01) ,我们还可以设置其他参数,调用方式如下:

MatrixFactorizationModel model2 = new  ALS().setRank(10)
                                            .setIterations(2000)
                                            .setLambda(0.01)
                                            .setImplicitPrefs(true)
                                            .setUserBlocks(10)
                                            .setProductBlocks(10)
                                            .run(training);
3.4、 利用模型进行预测

从 test训练集中获得只包含用户和商品的数据集 :

JavaRDD<Tuple2<Object, Object>>  testUsersProducts = test.map(line->{
            return new  Tuple2<>(line.user(),line.product());
});

使用训练好的推荐模型对用户商品进行预测评分,得到预测评分的数据集:

JavaPairRDD<Tuple2<Integer, Integer>, Double>  predictions =JavaPairRDD.fromJavaRDD(
          model.predict(testUsersProducts.rdd()).toJavaRDD().map(r->
                    new Tuple2<>(new  Tuple2<>(r.user(),r.product()),r.rating())));

将真实评分数据集与预测评分数据集进行合并。这里,Join操作类似于SQL的inner join操作,返回结果是前面和后面集合中配对成功的,过滤掉关联不上的。

JavaPairRDD<Tuple2<Integer, Integer>, Double>  predictions = JavaPairRDD.fromJavaRDD(
                model.predict(testUsersProducts.rdd()).toJavaRDD().map(r->
                    new Tuple2<>(new  Tuple2<>(r.user(),r.product()),r.rating())));
System.out.println(predictions);
predictions.foreach(x->{
    System.out.println(x);
});
/**
*控制台输出结果:
*Rating中的第一个int是user编号,第二个int是item编号,最后的double是user对item的评分。
----------------------------
((4,2),0.010526887751696495)
((1,1),3.6826782499237716)
((1,2),0.7464017268228036)
((3,2),0.010526887751696495)
----------------------------
**/

我们把结果输出,对比一下真实结果与预测结果:

JavaPairRDD<Tuple2<Integer, Integer>, Tuple2<Double, Double>> ratesAndPreds = JavaPairRDD.fromJavaRDD(test.map(x->new Tuple2<>(
        new Tuple2<>(x.user(),x.product()),x.rating()))).join(predictions);

System.out.println("------------------------------------------");
ratesAndPreds.foreach(x->{
    System.out.println(x);
});
/**
*控制台输出结果:
*比如,第二条结果记录((4,2),(5.0,0.010526887751696495))中,(4,2)分别表示4号用户和2号商品,而5.0是实际的估计分值,
*0.010526887751696495是经过推荐的预测分值。
-----------------------------------
((1,1),(5.0,3.6826782499237716))
((4,2),(5.0,0.010526887751696495))
((1,2),(1.0,0.7464017268228036))
((3,2),(5.0,0.010526887751696495))
-----------------------------------
**/

比如,第二条结果记录((4,2),(5.0,0.010526887751696495))中,(4,2)分别表示4号用户和2号商品,而5.0是实际的估计分值,0.010526887751696495是经过推荐的预测分值。
然后计算均方差,这里的r1就是真实结果,r2就是预测结果:

double MSE = ratesAndPreds.values().mapToDouble(x->{
    double err = x._1() - x._2();
    return err * err;
}).mean();
System.out.println("Mean Squared Error = "+MSE);
/**
*控制台输出结果:
-----------------------------------
Mean Squared Error = 2.8413113799948704
-----------------------------------
**/

我们可以看到打分的均方差值为1.09左右。由于本例的数据量很少,预测的结果和实际相比有一定的差距。上面的例子只是对测试集进行了评分,我们还可以进一步的通过调用model.recommendProducts给特定的用户推荐商品以及model.recommendUsers来给特定商品推荐潜在用户。

参考资料http://spark.apache.org/docs/latest/mllib-collaborative-filtering.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349