zoukankan      html  css  js  c++  java
  • 从源代码剖析Mahout推荐引擎

    本文来自于:http://blog.fens.me/mahout-recommend-engine/

    前言

          Mahout框架中cf.taste包实现了推荐算法引擎,它提供了一套完整的推荐算法工具集,同时规范了数据结构,并标准化了程序开发过程。应用推荐算法时,代码也就7-8行,简单地有点像R了。为了使用简单的目标,Mahout推荐引擎必然要做到精巧的程序设计。

         本文将介绍Mahout推荐引擎的程序设计。

    目录

    1. Mahout推荐引擎概况
    2. 标准化的程序开发过程
    3. 数据模型
    4. 相似度算法工具集
    5. 近邻算法工具集
    6. 推荐算法工具集
    7. 创建自己的推荐引擎构造器

    1. Mahout推荐引擎概况

       Mahout的推荐引擎,要从org.apache.mahout.cf.taste包说起

    mahout-core-class

    packages的说明:

    • common: 公共类包括,异常,数据刷新接口,权重常量
    • eval: 定义构造器接口,类似于工厂模式
    • model: 定义数据模型接口
    • neighborhood: 定义近邻算法的接口
    • recommender: 定义推荐算法的接口
    • similarity: 定义相似度算法的接口
    • transforms: 定义数据转换的接口
    • hadoop: 基于hadoop的分步式算法的实现类
    • impl: 单机内存算法实现类

    从上面的package情况,我可以粗略地看出推荐引擎分为5个主要部分组成:数据模型,相似度算法,近邻算法,推荐算法,算法评分器

    从数据处理能力上,算法可以分为:单机内存算法,基于hadoop的分步式算法。

    下面我们将基于单机内存算法,研究Mahout的推荐引擎的结构。

    2. 标准化的程序开发过程

    以UserCF的推荐算法为例,官方建议我们的开发过程:

    mahout_recommendation-process

    图片摘自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。

    3. 数据模型

    Mahout的推荐引擎的数据模型,以DataModel接口为父类。

     mahout-datamodel

    通过“策略模式”匹配不同的数据源,支持File, JDBC(MySQL, PostgreSQL), NoSQL(Cassandra, HBase, MongoDB)。

    注:NoSQL的实现在mahout-integration-0.8.jar中。

    数据格式支持2种:

    • GenericDataModel: 用户ID,物品ID,用户对物品的打分(UserID,ItemID,PreferenceValue)
    • GenericBooleanPrefDataModel: 用户ID,物品ID (UserID,ItemID),这种方式表达用户是否浏览过该物品,但并未对物品进行打分。

    mahout-pref

    4. 相似度算法工具集

    相似度算法分为2种

    • 基于用户(UserCF)的相似度算法
    • 基于物品(ItemCF)的相似度算法

    1). 基于用户(UserCF)的相似度算法

    mahout-UserSimilarity

    计算用户的相似矩阵,可以通过上图中几种算法。

    2). 基于物品(ItemCF)的相似度算法

    mahout-ItemSimilarity

    计算物品的相似矩阵,可以通过上图中几种算法。

    关于相似度距离的说明:

    • EuclideanDistanceSimilarity: 欧氏距离相似度

      image003

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

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

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

    • PearsonCorrelationSimilarity: 皮尔森相似度

      image004

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

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

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

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

    • UncenteredCosineSimilarity: 余弦相似度

      image005

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

      范围:[-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系数相似度

      image006

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

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

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

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

    5. 近邻算法工具集

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

    mahout-UserNeighborhood

    近邻算法分为2种:

    • NearestNUserNeighborhood:指定N的个数,比如,选出前10最相似的用户。
    • ThresholdUserNeighborhood:指定比例,比如,选择前10%最相似的用户

    mahout-Neighborhood

    6. 推荐算法工具集

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

    mahout-Recommender

    7. 创建自己的推荐引擎构造器

    有了上面的知识,我就清楚地知道了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
    ", 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
    ", 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
    ", stats);
    30         System.out.printf("Recommender IR Evaluator: [Precision:%s,Recall:%s]
    ", 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

  • 相关阅读:
    dockerfile构建的镜像中文乱码
    xshell+xmanager6破解
    mysql数据库本地登录无法查看到数据库
    Tomcat日志中文乱码问题解决
    Tomcat 启动很慢
    Supervisor-安装
    supervisor-monitor进程集中管理部署
    CeSi 进程集中管理部署
    [IOT]
    [SourceTree]
  • 原文地址:https://www.cnblogs.com/hellochennan/p/5409004.html
Copyright © 2011-2022 走看看