频道栏目
首页 > 资讯 > 其他综合 > 正文

mahout剖析

17-02-04        来源:[db:作者]  
收藏   我要投稿

mahout剖析:ahout是一个好用的推荐系统的工具,它是由apache公司的一个开源项目,其中它可以帮助我们实现一整套完整的推荐算法的工具集。
一、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 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();
    }

}
}

此代码中调用了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

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

从源代码剖析Mahout推荐引擎

近邻算法分为2种:

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

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

从源代码剖析Mahout推荐引擎

创建自己的推荐引擎构造器
有了上面的知识,我就清楚地知道了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));
        }
    };
}

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);
    }
}

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);
    }
}

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);
        }
    };
}

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());
}

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();
    }
}
相关TAG标签
上一篇:JS实现全选、不选和反选效果
下一篇:小程序-文字超出限制
相关文章
图文推荐

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站