标签:
本文来自于:http://blog.fens.me/mahout-recommend-engine/
前言
Mahout框架中cf.taste包实现了推荐算法引擎,它提供了一套完整的推荐算法工具集,同时规范了数据结构,并标准化了程序开发过程。应用推荐算法时,代码也就7-8行,简单地有点像R了。为了使用简单的目标,Mahout推荐引擎必然要做到精巧的程序设计。
本文将介绍Mahout推荐引擎的程序设计。
目录
Mahout的推荐引擎,要从org.apache.mahout.cf.taste包说起
packages的说明:
从上面的package情况,我可以粗略地看出推荐引擎分为5个主要部分组成:数据模型,相似度算法,近邻算法,推荐算法,算法评分器。
从数据处理能力上,算法可以分为:单机内存算法,基于hadoop的分步式算法。
下面我们将基于单机内存算法,研究Mahout的推荐引擎的结构。
以UserCF的推荐算法为例,官方建议我们的开发过程:
图片摘自Mahout in Action
从上图中我们可以看到,算法是被模块化的,通过1,2,3,4的过程进行方法调用。
程序代码:
1 public class UserCF {
2
3 final static int NEIGHBORHOOD_NUM = 2;
4 final static int RECOMMENDER_NUM = 3;
5
6 public static void main(String[] args) throws IOException, TasteException {
7 String file = "datafile/item.csv";
8 DataModel model = new FileDataModel(new File(file));
9 UserSimilarity user = new EuclideanDistanceSimilarity(model);
10 NearestNUserNeighborhood neighbor = new NearestNUserNeighborhood(NEIGHBORHOOD_NUM, user, model);
11 Recommender r = new GenericUserBasedRecommender(model, neighbor, user);
12 LongPrimitiveIterator iter = model.getUserIDs();
13
14 while (iter.hasNext()) {
15 long uid = iter.nextLong();
16 List list = r.recommend(uid, RECOMMENDER_NUM);
17 System.out.printf("uid:%s", uid);
18 for (RecommendedItem ritem : list) {
19 System.out.printf("(%s,%f)", ritem.getItemID(), ritem.getValue());
20 }
21 System.out.println();
22 }
23 }
24 }
我们调用算法的程序,要用到4个对象:DataModel, UserSimilarity, NearestNUserNeighborhood, Recommender。
Mahout的推荐引擎的数据模型,以DataModel接口为父类。
通过“策略模式”匹配不同的数据源,支持File, JDBC(MySQL, PostgreSQL), NoSQL(Cassandra, HBase, MongoDB)。
注:NoSQL的实现在mahout-integration-0.8.jar中。
数据格式支持2种:
相似度算法分为2种
1). 基于用户(UserCF)的相似度算法
计算用户的相似矩阵,可以通过上图中几种算法。
2). 基于物品(ItemCF)的相似度算法
计算物品的相似矩阵,可以通过上图中几种算法。
关于相似度距离的说明:
原理:利用欧式距离d定义的相似度s,s=1 / (1+d)。
范围:[0,1],值越大,说明d越小,也就是距离越近,则相似度越大。
说明:同皮尔森相似度一样,该相似度也没有考虑重叠数对结果的影响,同样地,Mahout通过增加一个枚举类型(Weighting)的参数来使得重叠数也成为计算相似度的影响因子。
原理:用来反映两个变量线性相关程度的统计量
范围:[-1,1],绝对值越大,说明相关性越强,负相关对于推荐的意义小。
说明:1、 不考虑重叠的数量;2、 如果只有一项重叠,无法计算相似性(计算过程被除数有n-1);3、 如果重叠的值都相等,也无法计算相似性(标准差为0,做除数)。
该相似度并不是最好的选择,也不是最坏的选择,只是因为其容易理解,在早期研究中经常被提起。使用Pearson线性相关系数必须假设数据是成对地从正态 分布中取得的,并且数据至少在逻辑范畴内必须是等间距的数据。Mahout中,为皮尔森相关计算提供了一个扩展,通过增加一个枚举类型 (Weighting)的参数来使得重叠数也成为计算相似度的影响因子。
原理:多维空间两点与所设定的点形成夹角的余弦值。
范围:[-1,1],值越大,说明夹角越大,两点相距就越远,相似度就越小。
说明:在数学表达中,如果对两个项的属性进行了数据中心化,计算出来的余弦相似度和皮尔森相似度是一样的,在mahout中,实现了数据中心化的过 程,所以皮尔森相似度值也是数据中心化后的余弦相似度。另外在新版本中,Mahout提供了UncenteredCosineSimilarity类作为 计算非中心化数据的余弦相似度。
原理:Spearman秩相关系数通常被认为是排列后的变量之间的Pearson线性相关系数。
范围:{-1.0,1.0},当一致时为1.0,不一致时为-1.0。
说明:计算非常慢,有大量排序。针对推荐系统中的数据集来讲,用Spearman秩相关系数作为相似度量是不合适的。
原理:曼哈顿距离的实现,同欧式距离相似,都是用于多维数据空间距离的测度
范围:[0,1],同欧式距离一致,值越小,说明距离值越大,相似度越大。
说明:比欧式距离计算量少,性能相对高。
原理:重叠的个数,不重叠的个数,都没有的个数
范围:具体可去百度文库中查找论文《Accurate Methods for the Statistics of Surprise and Coincidence》
说明:处理无打分的偏好数据,比Tanimoto系数的计算方法更为智能。
原理:又名广义Jaccard系数,是对Jaccard系数的扩展,等式为
范围:[0,1],完全重叠时为1,无重叠项时为0,越接近1说明越相似。
说明:处理无打分的偏好数据。
相似度算法介绍,摘自:http://www.cnblogs.com/dlts26/archive/2012/06/20/2555772.html
近邻算法只对于UserCF适用,通过近邻算法给相似的用户进行排序,选出前N个最相似的,作为最终推荐的参考的用户。
近邻算法分为2种:
推荐算法是以Recommender作为基础的父类,关于推荐算法的详细介绍,请参考文章:Mahout推荐算法API详解
有了上面的知识,我就清楚地知道了Mahout推荐引擎的原理和使用,我们就可以写一个自己的构造器,通过“策略模式”实现,算法的组合。
新建文件:org.conan.mymahout.recommendation.job.RecommendFactory.java
1 public final class RecommendFactory {
2 ...
3 }
1). 构造数据模型
1 public static DataModel buildDataModel(String file) throws TasteException, IOException {
2 return new FileDataModel(new File(file));
3 }
4
5 public static DataModel buildDataModelNoPref(String file) throws TasteException, IOException {
6 return new GenericBooleanPrefDataModel(GenericBooleanPrefDataModel.toDataMap(new FileDataModel(new File(file))));
7 }
8
9 public static DataModelBuilder buildDataModelNoPrefBuilder() {
10 return new DataModelBuilder() {
11 @Override
12 public DataModel buildDataModel(FastByIDMap trainingData) {
13 return new GenericBooleanPrefDataModel(GenericBooleanPrefDataModel.toDataMap(trainingData));
14 }
15 };
16 }
2). 构造相似度算法模型
1 public enum SIMILARITY {
2 PEARSON, EUCLIDEAN, COSINE, TANIMOTO, LOGLIKELIHOOD, FARTHEST_NEIGHBOR_CLUSTER, NEAREST_NEIGHBOR_CLUSTER
3 }
4
5 public static UserSimilarity userSimilarity(SIMILARITY type, DataModel m) throws TasteException {
6 switch (type) {
7 case PEARSON:
8 return new PearsonCorrelationSimilarity(m);
9 case COSINE:
10 return new UncenteredCosineSimilarity(m);
11 case TANIMOTO:
12 return new TanimotoCoefficientSimilarity(m);
13 case LOGLIKELIHOOD:
14 return new LogLikelihoodSimilarity(m);
15 case EUCLIDEAN:
16 default:
17 return new EuclideanDistanceSimilarity(m);
18 }
19 }
20
21 public static ItemSimilarity itemSimilarity(SIMILARITY type, DataModel m) throws TasteException {
22 switch (type) {
23 case LOGLIKELIHOOD:
24 return new LogLikelihoodSimilarity(m);
25 case TANIMOTO:
26 default:
27 return new TanimotoCoefficientSimilarity(m);
28 }
29 }
30
31 public static ClusterSimilarity clusterSimilarity(SIMILARITY type, UserSimilarity us) throws TasteException {
32 switch (type) {
33 case NEAREST_NEIGHBOR_CLUSTER:
34 return new NearestNeighborClusterSimilarity(us);
35 case FARTHEST_NEIGHBOR_CLUSTER:
36 default:
37 return new FarthestNeighborClusterSimilarity(us);
38 }
39 }
3). 构造近邻算法模型
1 public enum NEIGHBORHOOD {
2 NEAREST, THRESHOLD
3 }
4
5 public static UserNeighborhood userNeighborhood(NEIGHBORHOOD type, UserSimilarity s, DataModel m, double num) throws TasteException {
6 switch (type) {
7 case NEAREST:
8 return new NearestNUserNeighborhood((int) num, s, m);
9 case THRESHOLD:
10 default:
11 return new ThresholdUserNeighborhood(num, s, m);
12 }
13 }
4). 构造推荐算法模型
1 public enum RECOMMENDER {
2 USER, ITEM
3 }
4
5 public static RecommenderBuilder userRecommender(final UserSimilarity us, final UserNeighborhood un, boolean pref) throws TasteException {
6 return pref ? new RecommenderBuilder() {
7 @Override
8 public Recommender buildRecommender(DataModel model) throws TasteException {
9 return new GenericUserBasedRecommender(model, un, us);
10 }
11 } : new RecommenderBuilder() {
12 @Override
13 public Recommender buildRecommender(DataModel model) throws TasteException {
14 return new GenericBooleanPrefUserBasedRecommender(model, un, us);
15 }
16 };
17 }
18
19 public static RecommenderBuilder itemRecommender(final ItemSimilarity is, boolean pref) throws TasteException {
20 return pref ? new RecommenderBuilder() {
21 @Override
22 public Recommender buildRecommender(DataModel model) throws TasteException {
23 return new GenericItemBasedRecommender(model, is);
24 }
25 } : new RecommenderBuilder() {
26 @Override
27 public Recommender buildRecommender(DataModel model) throws TasteException {
28 return new GenericBooleanPrefItemBasedRecommender(model, is);
29 }
30 };
31 }
32
33 public static RecommenderBuilder slopeOneRecommender() throws TasteException {
34 return new RecommenderBuilder() {
35 @Override
36 public Recommender buildRecommender(DataModel dataModel) throws TasteException {
37 return new SlopeOneRecommender(dataModel);
38 }
39
40 };
41 }
42
43 public static RecommenderBuilder itemKNNRecommender(final ItemSimilarity is, final Optimizer op, final int n) throws TasteException {
44 return new RecommenderBuilder() {
45 @Override
46 public Recommender buildRecommender(DataModel dataModel) throws TasteException {
47 return new KnnItemBasedRecommender(dataModel, is, op, n);
48 }
49 };
50 }
51
52 public static RecommenderBuilder svdRecommender(final Factorizer factorizer) throws TasteException {
53 return new RecommenderBuilder() {
54 @Override
55 public Recommender buildRecommender(DataModel dataModel) throws TasteException {
56 return new SVDRecommender(dataModel, factorizer);
57 }
58 };
59 }
60
61 public static RecommenderBuilder treeClusterRecommender(final ClusterSimilarity cs, final int n) throws TasteException {
62 return new RecommenderBuilder() {
63 @Override
64 public Recommender buildRecommender(DataModel dataModel) throws TasteException {
65 return new TreeClusteringRecommender(dataModel, cs, n);
66 }
67 };
68 }
5). 构造算法评估模型
1 public enum EVALUATOR {
2 AVERAGE_ABSOLUTE_DIFFERENCE, RMS
3 }
4
5 public static RecommenderEvaluator buildEvaluator(EVALUATOR type) {
6 switch (type) {
7 case RMS:
8 return new RMSRecommenderEvaluator();
9 case AVERAGE_ABSOLUTE_DIFFERENCE:
10 default:
11 return new AverageAbsoluteDifferenceRecommenderEvaluator();
12 }
13 }
14
15 public static void evaluate(EVALUATOR type, RecommenderBuilder rb, DataModelBuilder mb, DataModel dm, double trainPt) throws TasteException {
16 System.out.printf("%s Evaluater Score:%s\n", type.toString(), buildEvaluator(type).evaluate(rb, mb, dm, trainPt, 1.0));
17 }
18
19 public static void evaluate(RecommenderEvaluator re, RecommenderBuilder rb, DataModelBuilder mb, DataModel dm, double trainPt) throws TasteException {
20 System.out.printf("Evaluater Score:%s\n", re.evaluate(rb, mb, dm, trainPt, 1.0));
21 }
22
23 /**
24 * statsEvaluator
25 */
26 public static void statsEvaluator(RecommenderBuilder rb, DataModelBuilder mb, DataModel m, int topn) throws TasteException {
27 RecommenderIRStatsEvaluator evaluator = new GenericRecommenderIRStatsEvaluator();
28 IRStatistics stats = evaluator.evaluate(rb, mb, m, null, topn, GenericRecommenderIRStatsEvaluator.CHOOSE_THRESHOLD, 1.0);
29 // System.out.printf("Recommender IR Evaluator: %s\n", stats);
30 System.out.printf("Recommender IR Evaluator: [Precision:%s,Recall:%s]\n", stats.getPrecision(), stats.getRecall());
31 }
6). 推荐结果输出
1 public static void showItems(long uid, List recommendations, boolean skip) {
2 if (!skip || recommendations.size() > 0) {
3 System.out.printf("uid:%s,", uid);
4 for (RecommendedItem recommendation : recommendations) {
5 System.out.printf("(%s,%f)", recommendation.getItemID(), recommendation.getValue());
6 }
7 System.out.println();
8 }
9 }
7). 完整源代码文件及使用样例:
https://github.com/bsspirit/maven_mahout_template/tree/mahout-0.8/src/main/java/org/conan/mymahout/recommendation/job
标签:
原文地址:http://www.cnblogs.com/hellochennan/p/5409004.html