mahout总体概览

一、mahout推荐系统概况 
mahout的包 
这里写图片描述 
包中的每一个类说明 
common:公共类包,包括:异常、数据刷新接口、权重常量 
model: 定义数据模型接口 
neighborhood: 定义近邻算法的接口 
recommender: 定义推荐算法的接口 
similarity: 定义相似度算法的接口 
transforms: 定义数据转换的接口 
hadoop: 基于hadoop的分步式算法的实现类 
impl: 单机内存算法实现类 
从上面的package情况,我可以粗略地看出推荐引擎分为5个主要部分组成:数据模型,相似度算法,近邻算法,推荐算法,算法评分器。 
其中,从数据处理能力上看,算法又可以分为:单机内存算法、基于hadoop的分布式算法。

二、标准化的程序开发过程 
这里写图片描述 
算法都是模块化的,通过1,2,3,4的过程进行调用。 
例如:基于最经典的用户的协同过滤算法的java代码如下所示:

package com.sunwangdong;

import org.apache.mahout.cf.taste.common.TasteException; 
import org.apache.mahout.cf.taste.impl.model.file.FileDataModel; 
import org.apache.mahout.cf.taste.model.DataModel; 
import org.apache.mahout.cf.taste.similarity.UserSimilarity;

import org.apache.mahout.cf.taste.impl.similarity.EuclideanDistanceSimilarity; 
import org.apache.mahout.cf.taste.impl.neighborhood.NearestNUserNeighborhood; 
import org.apache.mahout.cf.taste.impl.recommender.GenericUserBasedRecommender; 
import org.apache.mahout.cf.taste.impl.common.LongPrimitiveIterator; 
import org.apache.mahout.cf.taste.recommender.*;

import java.io.File; 
import java.io.IOException; 
import java.util.List;

public class Main 

final static int NEIGHBORHOOD_NUM = 2; //用户邻居数量 
final static int RECOMMENDER_NUM = 3; //推荐结果个数

public static void main(String[] args) throws IOException,TasteException
{
// write your code here

    String file = "datafile/data.csv";   //数据集,其中第一列表示用户id;第二列表示商品id;第三列表示评分,评分是5分制
    DataModel model = new FileDataModel(new File(file));  //基于文件的model,通过文件形式来读入,且此类型所需要读入的数据的格式要求很低,只需要满足每一行是用户id,物品id,用户偏好,且之间用tab或者是逗号隔开即可

    //基于用户的协同过滤算法,基于物品的协同过滤算法

    UserSimilarity user = new EuclideanDistanceSimilarity(model);  //计算欧式距离,欧式距离来定义相似性,用s=1/(1+d)来表示,范围在[0,1]之间,值越大,表明d越小,距离越近,则表示相似性越大
    NearestNUserNeighborhood neighbor = new NearestNUserNeighborhood(NEIGHBORHOOD_NUM, user, model);
    //指定用户邻居数量

    //构建基于用户的推荐系统
    Recommender r = new GenericUserBasedRecommender(model, neighbor, user);


    //得到所有用户的id集合
    LongPrimitiveIterator iter = model.getUserIDs();

    while(iter.hasNext())
    {
        long uid = iter.nextLong();
        List<RecommendedItem> list = r.recommend(uid,RECOMMENDER_NUM);  //获取推荐结果
        System.out.printf("uid:%s",uid);
        //遍历推荐结果
        for(RecommendedItem ritem : list)
        {
            System.out.printf("(%s,%f)",ritem.getItemID(),ritem.getValue());
        }
        System.out.println();
    }

}
  • 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


此代码中调用了4个对象,分别是datamodel,usersimilarity,nearestnuserneighborhood,recommender。

三、数据模型 
mahout的推荐系统的数据模型,以datamodel接口为父类。 
这里写图片描述
此外,通过“策略模式“匹配不同的数据源,支持file、jdbc、nosql等不同的类型。

数据格式支持两种: 
genericdatamodel:用户id,物品id,用户对物品的打分 
genericbooleanprefdatamodel:用户id,物品id,这种方式表示用户是否浏览过该物品,但并未对该物品进行打分。 
这里写图片描述

四、相似度算法工具箱 
相似度分为两种: 
1、基于用户的相似度算法(usercf) 
这里写图片描述 
计算用户的相似矩阵,就可以通过上述几种算法来实现。

2、基于物品的相似度算法(itemcf) 
这里写图片描述
计算物品的相似度,就可以利用上述几种算法。 
qizhon 
EuclideanDistanceSimilarity: 欧氏距离相似度 
从源代码剖析Mahout推荐引擎

原理:利用欧式距离d定义的相似度s,s=1 / (1+d)。

范围:[0,1],值越大,说明d越小,也就是距离越近,则相似度越大。

说明:同皮尔森相似度一样,该相似度也没有考虑重叠数对结果的影响,同样地,Mahout通过增加一个枚举类型(Weighting)的参数来使得重叠数也成为计算相似度的影响因子。

PearsonCorrelationSimilarity: 皮尔森相似度 
从源代码剖析Mahout推荐引擎

原理:用来反映两个变量线性相关程度的统计量

范围:[-1,1],绝对值越大,说明相关性越强,负相关对于推荐的意义小。

说明:1、 不考虑重叠的数量;2、 如果只有一项重叠,无法计算相似性(计算过程被除数有n-1);3、 如果重叠的值都相等,也无法计算相似性(标准差为0,做除数)。

该相似度并不是最好的选择,也不是最坏的选择,只是因为其容易理解,在早期研究中经常被提起。使用Pearson线性相关系数必须假设数据是成对地从正态 分布中取得的,并且数据至少在逻辑范畴内必须是等间距的数据。Mahout中,为皮尔森相关计算提供了一个扩展,通过增加一个枚举类型 (Weighting)的参数来使得重叠数也成为计算相似度的影响因子。

UncenteredCosineSimilarity: 余弦相似度 
从源代码剖析Mahout推荐引擎

原理:多维空间两点与所设定的点形成夹角的余弦值。

范围:[-1,1],值越大,说明夹角越大,两点相距就越远,相似度就越小。

说明:在数学表达中,如果对两个项的属性进行了数据中心化,计算出来的余弦相似度和皮尔森相似度是一样的,在mahout中,实现了数据中心化的过 程,所以皮尔森相似度值也是数据中心化后的余弦相似度。另外在新版本中,Mahout提供了UncenteredCosineSimilarity类作为 计算非中心化数据的余弦相似度。

SpearmanCorrelationSimilarity: Spearman秩相关系数相似度 
原理:Spearman秩相关系数通常被认为是排列后的变量之间的Pearson线性相关系数。

范围:{-1.0,1.0},当一致时为1.0,不一致时为-1.0。

说明:计算非常慢,有大量排序。针对推荐系统中的数据集来讲,用Spearman秩相关系数作为相似度量是不合适的。

CityBlockSimilarity: 曼哈顿距离相似度 
原理:曼哈顿距离的实现,同欧式距离相似,都是用于多维数据空间距离的测度

范围:[0,1],同欧式距离一致,值越小,说明距离值越大,相似度越大。

说明:比欧式距离计算量少,性能相对高。

LogLikelihoodSimilarity: 对数似然相似度 
原理:重叠的个数,不重叠的个数,都没有的个数

范围:具体可去百度文库中查找论文《Accurate Methods for the Statistics of Surprise and Coincidence》

说明:处理无打分的偏好数据,比Tanimoto系数的计算方法更为智能。

TanimotoCoefficientSimilarity: Tanimoto系数相似度 
从源代码剖析Mahout推荐引擎

原理:又名广义Jaccard系数,是对Jaccard系数的扩展,等式为

范围:[0,1],完全重叠时为1,无重叠项时为0,越接近1说明越相似。

说明:处理无打分的偏好数据。

相似度算法介绍,摘自:http://www.cnblogs.com/dlts26/archive/2012/06/20/2555772.html

  1. 近邻算法工具集 
    近邻算法只对于UserCF适用,通过近邻算法给相似的用户进行排序,选出前N个最相似的,作为最终推荐的参考的用户。

从源代码剖析Mahout推荐引擎

近邻算法分为2种:

NearestNUserNeighborhood:指定N的个数,比如,选出前10最相似的用户。 
ThresholdUserNeighborhood:指定比例,比如,选择前10%最相似的用户。 
从源代码剖析Mahout推荐引擎

  1. 推荐算法工具集 
    推荐算法是以Recommender作为基础的父类,关于推荐算法的详细介绍,请参考文章:Mahout推荐算法API详解

从源代码剖析Mahout推荐引擎

  1. 创建自己的推荐引擎构造器 
    有了上面的知识,我就清楚地知道了Mahout推荐引擎的原理和使用,我们就可以写一个自己的构造器,通过“策略模式”实现,算法的组合。

新建文件:org.conan.mymahout.recommendation.job.RecommendFactory.java

public final class RecommendFactory { 
… 

1). 构造数据模型

 public static DataModel buildDataModel(String file) throws TasteException, IOException {
    return new FileDataModel(new File(file));
}

public static DataModel buildDataModelNoPref(String file) throws TasteException, IOException {
    return new GenericBooleanPrefDataModel(GenericBooleanPrefDataModel.toDataMap(new FileDataModel(new File(file))));
}

public static DataModelBuilder buildDataModelNoPrefBuilder() {
    return new DataModelBuilder() {
        @Override
        public DataModel buildDataModel(FastByIDMap trainingData) {
            return new GenericBooleanPrefDataModel(GenericBooleanPrefDataModel.toDataMap(trainingData));
        }
    };
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

2). 构造相似度算法模型

public enum SIMILARITY { 
PEARSON, EUCLIDEAN, COSINE, TANIMOTO, LOGLIKELIHOOD, FARTHEST_NEIGHBOR_CLUSTER, NEAREST_NEIGHBOR_CLUSTER 
}

public static UserSimilarity userSimilarity(SIMILARITY type, DataModel m) throws TasteException {
    switch (type) {
    case PEARSON:
        return new PearsonCorrelationSimilarity(m);
    case COSINE:
        return new UncenteredCosineSimilarity(m);
    case TANIMOTO:
        return new TanimotoCoefficientSimilarity(m);
    case LOGLIKELIHOOD:
        return new LogLikelihoodSimilarity(m);
    case EUCLIDEAN:
    default:
        return new EuclideanDistanceSimilarity(m);
    }
}

public static ItemSimilarity itemSimilarity(SIMILARITY type, DataModel m) throws TasteException {
    switch (type) {
    case LOGLIKELIHOOD:
        return new LogLikelihoodSimilarity(m);
    case TANIMOTO:
    default:
        return new TanimotoCoefficientSimilarity(m);
    }
}

public static ClusterSimilarity clusterSimilarity(SIMILARITY type, UserSimilarity us) throws TasteException {
    switch (type) {
    case NEAREST_NEIGHBOR_CLUSTER:
        return new NearestNeighborClusterSimilarity(us);
    case FARTHEST_NEIGHBOR_CLUSTER:
    default:
        return new FarthestNeighborClusterSimilarity(us);
    }
}
  • 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

3). 构造近邻算法模型

public enum NEIGHBORHOOD { 
NEAREST, THRESHOLD 
}

public static UserNeighborhood userNeighborhood(NEIGHBORHOOD type, UserSimilarity s, DataModel m, double num) throws TasteException {
    switch (type) {
    case NEAREST:
        return new NearestNUserNeighborhood((int) num, s, m);
    case THRESHOLD:
    default:
        return new ThresholdUserNeighborhood(num, s, m);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

4). 构造推荐算法模型

public enum RECOMMENDER { 
USER, ITEM 
}

public static RecommenderBuilder userRecommender(final UserSimilarity us, final UserNeighborhood un, boolean pref) throws TasteException {
    return pref ? new RecommenderBuilder() {
        @Override
        public Recommender buildRecommender(DataModel model) throws TasteException {
            return new GenericUserBasedRecommender(model, un, us);
        }
    } : new RecommenderBuilder() {
        @Override
        public Recommender buildRecommender(DataModel model) throws TasteException {
            return new GenericBooleanPrefUserBasedRecommender(model, un, us);
        }
    };
}

public static RecommenderBuilder itemRecommender(final ItemSimilarity is, boolean pref) throws TasteException {
    return pref ? new RecommenderBuilder() {
        @Override
        public Recommender buildRecommender(DataModel model) throws TasteException {
            return new GenericItemBasedRecommender(model, is);
        }
    } : new RecommenderBuilder() {
        @Override
        public Recommender buildRecommender(DataModel model) throws TasteException {
            return new GenericBooleanPrefItemBasedRecommender(model, is);
        }
    };
}

public static RecommenderBuilder slopeOneRecommender() throws TasteException {
    return new RecommenderBuilder() {
        @Override
        public Recommender buildRecommender(DataModel dataModel) throws TasteException {
            return new SlopeOneRecommender(dataModel);
        }

    };
}

public static RecommenderBuilder itemKNNRecommender(final ItemSimilarity is, final Optimizer op, final int n) throws TasteException {
    return new RecommenderBuilder() {
        @Override
        public Recommender buildRecommender(DataModel dataModel) throws TasteException {
            return new KnnItemBasedRecommender(dataModel, is, op, n);
        }
    };
}

public static RecommenderBuilder svdRecommender(final Factorizer factorizer) throws TasteException {
    return new RecommenderBuilder() {
        @Override
        public Recommender buildRecommender(DataModel dataModel) throws TasteException {
            return new SVDRecommender(dataModel, factorizer);
        }
    };
}

public static RecommenderBuilder treeClusterRecommender(final ClusterSimilarity cs, final int n) throws TasteException {
    return new RecommenderBuilder() {
        @Override
        public Recommender buildRecommender(DataModel dataModel) throws TasteException {
            return new TreeClusteringRecommender(dataModel, cs, n);
        }
    };
}
  • 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

5). 构造算法评估模型

public enum EVALUATOR { 
AVERAGE_ABSOLUTE_DIFFERENCE, RMS 
}

public static RecommenderEvaluator buildEvaluator(EVALUATOR type) {
    switch (type) {
    case RMS:
        return new RMSRecommenderEvaluator();
    case AVERAGE_ABSOLUTE_DIFFERENCE:
    default:
        return new AverageAbsoluteDifferenceRecommenderEvaluator();
    }
}

public static void evaluate(EVALUATOR type, RecommenderBuilder rb, DataModelBuilder mb, DataModel dm, double trainPt) throws TasteException {
    System.out.printf("%s Evaluater Score:%s\n", type.toString(), buildEvaluator(type).evaluate(rb, mb, dm, trainPt, 1.0));
}

public static void evaluate(RecommenderEvaluator re, RecommenderBuilder rb, DataModelBuilder mb, DataModel dm, double trainPt) throws TasteException {
    System.out.printf("Evaluater Score:%s\n", re.evaluate(rb, mb, dm, trainPt, 1.0));
}

/**
 * statsEvaluator
 */
public static void statsEvaluator(RecommenderBuilder rb, DataModelBuilder mb, DataModel m, int topn) throws TasteException {
    RecommenderIRStatsEvaluator evaluator = new GenericRecommenderIRStatsEvaluator();
    IRStatistics stats = evaluator.evaluate(rb, mb, m, null, topn, GenericRecommenderIRStatsEvaluator.CHOOSE_THRESHOLD, 1.0);
    // System.out.printf("Recommender IR Evaluator: %s\n", stats);
    System.out.printf("Recommender IR Evaluator: [Precision:%s,Recall:%s]\n", stats.getPrecision(), stats.getRecall());
}
  • 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

6). 推荐结果输出

 public static void showItems(long uid, List recommendations, boolean skip) {
    if (!skip || recommendations.size() > 0) {
        System.out.printf("uid:%s,", uid);
        for (RecommendedItem recommendation : recommendations) {
            System.out.printf("(%s,%f)", recommendation.getItemID(), recommendation.getValue());
        }
        System.out.println();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

7). 完整源代码文件及使用样例: 
https://github.com/bsspirit/maven_mahout_template/tree/mahout-0.8/src/main/java/org/conan/mymahout/recommendation/job

转载请注明出处: 
http://blog.fens.me/mahout-recommend-engine/

猜你喜欢

转载自blog.csdn.net/sujins5288/article/details/86217361
今日推荐