Mahout开发实战(二)

149 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第14天,点击查看活动详情

1、前言

上一篇为大家介绍了Mahout开发中Service层的一些示例代码。本次继续为大家介绍另一大部分,推荐器的示例代码。

2、Recommender构造器

这里以基于用户的推荐构造器为例:

/**
 * @Title: ItemBasedRecommenderBuilder
 * @Description: 基于用户的推荐 Recommender 构造器
 */
public class UserBasedRecommenderBuilder implements RecommenderBuilder {

    /**
     * 相似度
     */
    private UserSimilarity userSimilarity;

    /**
     * 邻域
     */
    private UserNeighborhood userNeighborhood;

    /**
     * 邻居个数,默认10
     */
    private int neighborhoodNumber;

    public UserBasedRecommenderBuilder() {

    }

    public UserBasedRecommenderBuilder(DataModel dataModel, final int neighborhoodNumber) throws TasteException {
        // 邻居个数,默认10
        this.neighborhoodNumber = neighborhoodNumber;
        //相似度矩阵、邻居用户采用默认实现
        this.userSimilarity = new PearsonCorrelationSimilarity(dataModel);
        this.userNeighborhood = new NearestNUserNeighborhood(neighborhoodNumber, this.userSimilarity, dataModel);
    }

    @Override
    public Recommender buildRecommender(DataModel dataModel) throws TasteException {
        return new CachingRecommender(new GenericUserBasedRecommender(dataModel, userNeighborhood, userSimilarity));
    }

}

3、定义Recommender接口

定义一个统一的Recommender接口,包括推荐、查准率、查全率等方法。

/**
 * @Title: IRecommender
 * @Description: IRecommender
 * @Company: huayi
 */
public interface IRecommender extends Recommender {

    /**
     * 采用默认的紧邻计算,推荐默认条数据
     * @param userId
     * @return
     * @throws TasteException
     */
    List<RecommendedItem> recommend(final long userId) throws TasteException;

    /**
     * 计算评分、评估推荐引擎的准确性
     * @return
     * @throws TasteException
     */
    double evaluateScore() throws TasteException;

    /**
     * 计算评分
     * @param recommenderBuilder
     * @param dataModel
     * @param trainingPercentage
     * @param evaluationPercentage
     * @return
     * @throws TasteException
     */
    double evaluateScore(RecommenderBuilder recommenderBuilder, DataModel dataModel,
                         double trainingPercentage, double evaluationPercentage) throws TasteException;

    /**
     * 计算查全率、查准率等
     * @return
     * @throws TasteException
     */
    IRStatistics evaluateIRStatistics() throws TasteException;

    /**
     * 计算查全率和查准率
     * @param recommenderBuilder
     * @param dataModel
     * @param rescorer
     * @param at
     * @param relevanceThreshold
     * @param evaluationPercentage
     * @return
     * @throws TasteException
     */
    IRStatistics evaluateIRStatistics(RecommenderBuilder recommenderBuilder,
                                      DataModel dataModel,
                                      IDRescorer rescorer,
                                      int at,
                                      double relevanceThreshold,
                                      double evaluationPercentage) throws TasteException;


}

4、定义抽象Recommender

抽象Recommender包括共通的评分、查全率、查准率方法的实现。

/**
 * @Title: AbstractRecommender
 * @Description: 抽象 Recommender 类
 */
public abstract class AbstractRecommender implements IRecommender {

    /**
     * Recommender构造器
     */
    protected RecommenderBuilder recommenderBuilder;

    /**
     * recommender
     */
    protected Recommender recommender;

    /**
     * 数据模型
     */
    protected DataModel dataModel;

    @Override
    public double evaluateScore() throws TasteException {
        return RecommendeEvaluator.evaluateScore(recommenderBuilder, dataModel);
    }


    @Override
    public double evaluateScore(RecommenderBuilder recommenderBuilder, DataModel dataModel,
                                double trainingPercentage, double evaluationPercentage) throws TasteException {
        return RecommendeEvaluator.evaluateScore(recommenderBuilder, dataModel, trainingPercentage,
                evaluationPercentage);
    }

    @Override
    public IRStatistics evaluateIRStatistics() throws TasteException {
        return RecommendeEvaluator.evaluateIRStatistics(recommenderBuilder, dataModel);
    }

    /**
     * 计算查全率和查准率
     * @param recommenderBuilder   推荐算法构造器
     * @param dataModel            数据模型
     * @param rescorer             如果有的话,在计算推荐时使用
     * @param at                   如,“精确度为5”。评估精度时要考虑的建议数量,
     * @param relevanceThreshold   优先值至少为该值的项目被认为是“相关的”用于计算的目的
     * @param evaluationPercentage 评估百分比
     * @return
     * @throws TasteException
     */
    @Override
    public IRStatistics evaluateIRStatistics(RecommenderBuilder recommenderBuilder,
                                             DataModel dataModel,
                                             IDRescorer rescorer,
                                             int at,
                                             double relevanceThreshold,
                                             double evaluationPercentage) throws TasteException {
        return RecommendeEvaluator.evaluateIRStatistics(
                recommenderBuilder, dataModel, rescorer, at, relevanceThreshold, evaluationPercentage);
    }

}

5、定义Recommender实现类

这里还是以基于用户的推荐为例。

/**
 * @Title: UserBasedCfRecommender
 * @Description: 基于用户的推荐 Recommender 类
 */
public class UserBasedCfRecommender extends AbstractRecommender {

    public UserBasedCfRecommender() {

    }

    /**
     * UserBasedCFRecommender
     * @param dataModel
     * @throws TasteException
     */
    public UserBasedCfRecommender(final DataModel dataModel) throws TasteException {
        defaultInit(dataModel, NumericConstant.TWENTY);
    }

    /**
     * 默认初始化方法
     * @param dataModel          数据模型
     * @param neighborhoodNumber 邻居个数
     * @throws TasteException
     */
    private void defaultInit(final DataModel dataModel, final int neighborhoodNumber) throws TasteException {
        this.dataModel = dataModel;
        this.recommenderBuilder = new UserBasedRecommenderBuilder(dataModel, neighborhoodNumber);
        this.recommender = recommenderBuilder.buildRecommender(this.dataModel);
    }

    /**
     * UserBasedCFRecommender
     * @param dataModel            --数据源
     * @param neighborhoodNumber   --邻域
     * @throws TasteException
     */
    public UserBasedCfRecommender(DataModel dataModel, final int neighborhoodNumber) throws TasteException {
        defaultInit(dataModel, neighborhoodNumber);
    }

    @Override
    public List<RecommendedItem> recommend(final long userId) throws TasteException {
        //默认推荐1条数据
        return this.recommend(userId, NumericConstant.ONE);
    }

    @Override
    public List<RecommendedItem> recommend(final long userId, final int recommendNum) throws TasteException {
        return this.recommender.recommend(userId, recommendNum);
    }

    /**
     * @param userId
     * @param recommendNum
     * @param includeKnownItems  --是否在推荐中包含用户已经获取过的内容
     * @return
     * @throws TasteException
     */
    @Override
    public List<RecommendedItem> recommend(long userId, int recommendNum, boolean includeKnownItems) throws TasteException {
        return this.recommender.recommend(userId, recommendNum, includeKnownItems);
    }

    /**
     * @param userId
     * @param recommendNum
     * @param idRescorer  --在确定最终推荐列表之前应用的评分函数
     * @return
     * @throws TasteException
     */
    @Override
    public List<RecommendedItem> recommend(long userId, int recommendNum, IDRescorer idRescorer) throws TasteException {
        return this.recommender.recommend(userId, recommendNum, idRescorer, false);
    }

    @Override
    public List<RecommendedItem> recommend(long userId, int recommendNum, IDRescorer idRescorer, boolean includeKnownItems) throws TasteException {
        return this.recommender.recommend(userId, recommendNum, idRescorer, includeKnownItems);
    }

    /**
     * 估计偏好,如果用户没有表示对该项目的偏好,或者用户对该项目的实际偏好。如果无法估计首选项,则返回Double.NaN
     * @param userId
     * @param itemId  --要估计偏好的项目ID
     * @return
     * @throws TasteException
     */
    @Override
    public float estimatePreference(long userId, long itemId) throws TasteException {
        return this.recommender.estimatePreference(userId, itemId);
    }

    @Override
    public void setPreference(long userID, long itemID, float value) throws TasteException {
        this.recommender.setPreference(userID, itemID, value);
    }

    @Override
    public void removePreference(long userID, long itemID) throws TasteException {
        this.recommender.removePreference(userID, itemID);
    }

    @Override
    public DataModel getDataModel() {
        return this.recommender.getDataModel();
    }

    @Override
    public void refresh(Collection<Refreshable> alreadyRefreshed) {
        this.recommender.refresh(alreadyRefreshed);
    }
}

最后,其实上述封装代码知识为了符合MVC的开发思想,从代码结构上更加清晰明了。好了、本期就先介绍到这里,有什么需要交流的,大家可以随时私信我。😊