轉】從源代碼剖析Mahout推薦引擎

  原博文出自於:  http://blog.fens.me/mahout-recommend-engine/      感謝!html

 

 

 

從源代碼剖析Mahout推薦引擎

Hadoop家族系列文章,主要介紹Hadoop家族產品,經常使用的項目包括Hadoop, Hive, Pig, HBase, Sqoop, Mahout, Zookeeper, Avro, Ambari, Chukwa,新增長的項目包括,YARN, Hcatalog, Oozie, Cassandra, Hama, Whirr, Flume, Bigtop, Crunch, Hue等。java

從2011年開始,中國進入大數據風起雲涌的時代,以Hadoop爲表明的家族軟件,佔據了大數據處理的廣闊地盤。開源界及廠商,全部數據軟件,無一不向Hadoop靠攏。Hadoop也從小衆的高富帥領域,變成了大數據開發的標準。在Hadoop原有技術基礎之上,出現了Hadoop家族產品,經過「大數據」概念不斷創新,推出科技進步。git

做爲IT界的開發人員,咱們也要跟上節奏,抓住機遇,跟着Hadoop一塊兒雄起!程序員

關於做者:github

  • 張丹(Conan), 程序員Java,R,PHP,Javascript
  • weibo:@Conan_Z
  • blog: http://blog.fens.me
  • email: bsspirit@gmail.com

轉載請註明出處:
http://blog.fens.me/mahout-recommend-engine/算法

mahout-recommender-engine

前言apache

Mahout框架中cf.taste包實現了推薦算法引擎,它提供了一套完整的推薦算法工具集,同時規範了數據結構,並標準化了程序開發過程。應用推薦算法時,代碼也就7-8行,簡單地有點像R了。爲了使用簡單的目標,Mahout推薦引擎必然要作到精巧的程序設計。api

本文將介紹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的過程進行方法調用。

程序代碼:

public class UserCF {

    final static int NEIGHBORHOOD_NUM = 2;
    final static int RECOMMENDER_NUM = 3;

    public static void main(String[] args) throws IOException, TasteException {
        String file = "datafile/item.csv";
        DataModel model = new FileDataModel(new File(file));
        UserSimilarity user = new EuclideanDistanceSimilarity(model);
        NearestNUserNeighborhood neighbor = new NearestNUserNeighborhood(NEIGHBORHOOD_NUM, user, model);
        Recommender r = new GenericUserBasedRecommender(model, neighbor, user);
        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。

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],絕對值越大,說明相關性越強,負相關對於推薦的意義小。

    說明:一、 不考慮重疊的數量;二、 若是隻有一項重疊,沒法計算類似性(計算過程被除數有n-1);三、 若是重疊的值都相等,也沒法計算類似性(標準差爲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

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

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/

相關文章
相關標籤/搜索