mahout算法源碼分析之Collaborative Filtering with ALS-WR (四)評價和推薦

Mahout版本:0.7,hadoop版本:1.0.4,jdk:1.7.0_25 64bit。java

首先來總結一下 mahout算法源碼分析之Collaborative Filtering with ALS-WR (三),這個寫了三篇,基本都是寫QR分解,而後矩陣進過處理獲得U或者M的過程,可是仍是沒有講出個因此然來。mahout官網上說其是根據這篇文獻得來的Large-scale Parallel Collaborative Filtering for the Netflix Prize,原本我是想翻譯這篇來着,就是爲了想弄清楚這個所謂的QR分解算法,可是感受好像和mahout中的實現不一樣。(這篇在csdn上面已經有人翻譯了,可是真的只是翻譯而已,並無進行講解,結果仍是本身來看),其中主要的是下面的matlab部分代碼:算法


這裏是使用給定的U來更新M,mahout中第一個是用M--1來更新U-0,其實都是同樣的。看上面的圖,其中的vector其實就是Vi,而matrix其實就是Ai(對應於前篇blog中相應的變量),因此matrix\vector 其實就至關於Ai\Vi = Ai‘*Vi(Ai的逆矩陣和Vi相乘)這樣就能夠獲得M。額,或許這裏的mahout代碼就是求Ai的逆矩陣?恩,頗有可能這樣的話,應該是對到了。只是這裏求Ai的逆矩陣代碼太複雜了。這個有時間仍是要慢慢研究下的。apache

今天原本就不打算糾結這個問題了,沒想到又看一遍竟然好像有點領悟了(以前一直覺得逆矩陣和轉置矩陣同樣的,致使覺得mahout代碼和matlab代碼不符,哎,仍是矩陣沒學好。。。)app

本篇正文:評價這個算法,在mahout中其實就是使用了一個參數而已,rmse,均方根偏差。運行這一步其實就是先使用前面一步得到的U和M來對每一個用戶應經評價的movies進行從新評價,即評價預測。而後把真是評價分數和預測評價分數的偏差寫入文件。最後讀出偏差列,把其求平均獲得最後偏差結果。在mahout中評價使用的job文件是:org.apache.mahout.cf.taste.hadoop.als.FactorizationEvaluator。打開這個文件,能夠看到在run方法中有一個parepareJob的函數,同時這個函數的Mapper是PredictRatingsMapper。打開這個PredictRatingsMapper能夠看到它有setup和map函數,setup函數主要是把路徑U和M中的數據load到一個變量裏面,map是主要操做,源碼以下:ide

 

 protected void map(LongWritable key, Text value, Context ctx) throws IOException, InterruptedException {

      String[] tokens = TasteHadoopUtils.splitPrefTokens(value.toString());
      int userID = Integer.parseInt(tokens[0]);
      int itemID = Integer.parseInt(tokens[1]);
      double rating = Double.parseDouble(tokens[2]);

      if (U.containsKey(userID) && M.containsKey(itemID)) {
        double estimate = U.get(userID).dot(M.get(itemID));
        double err = rating - estimate;
        ctx.write(new DoubleWritable(err), NullWritable.get());
      }
    }

額 ,好吧首先說下這個輸入數據,格式爲:userid,itemid,rating。而後map遍歷每條記錄,首先把當前記錄中的userID、ItemID和rating提取出來,而後從U和M中分別取出userid和itemid對應的行或列向量,取出來後,把這兩個向量作點乘便可獲得最後的預測評價分數。而後使用是真實的評價分數減去預測的評價分數便可獲得偏差,進行輸出便可。在這個job運行完成後,隨機代碼就調用了computeRmse函數來對輸出文件進行分析,求出最後的rmse:函數

 

 

protected double computeRmse(Path errors) {
    RunningAverage average = new FullRunningAverage();
    for (Pair<DoubleWritable,NullWritable> entry :
      new SequenceFileDirIterable<DoubleWritable, NullWritable>(errors, PathType.LIST, PathFilters.logsCRCFilter(),
          getConf())) {
      DoubleWritable error = entry.getFirst();
      average.addDatum(error.get() * error.get());
    }

    return Math.sqrt(average.getAverage());
  }

而後是推薦,推薦使用的源碼是在:org.apache.mahout.cf.taste.hadoop.als.RecommenderJob。這裏run方法中的prepareJob中使用的mapper是PredictionMapper。這個mapper中一樣含有setup和map函數,其中的setup函數仍是獲取U和M,不過還額外獲取了一個MaxRating變量。map中的處理比較多,先貼源碼吧:oop

 

 

protected void map(IntWritable userIDWritable, VectorWritable ratingsWritable, Context ctx)
        throws IOException, InterruptedException {

      Vector ratings = ratingsWritable.get();
      final int userID = userIDWritable.get();
      final OpenIntHashSet alreadyRatedItems = new OpenIntHashSet(ratings.getNumNondefaultElements());
      final TopK<RecommendedItem> topKItems = new TopK<RecommendedItem>(recommendationsPerUser, BY_PREFERENCE_VALUE);

      Iterator<Vector.Element> ratingsIterator = ratings.iterateNonZero();
      while (ratingsIterator.hasNext()) {
        alreadyRatedItems.add(ratingsIterator.next().index());
      }

      M.forEachPair(new IntObjectProcedure<Vector>() {
        @Override
        public boolean apply(int itemID, Vector itemFeatures) {
          if (!alreadyRatedItems.contains(itemID)) {
            double predictedRating = U.get(userID).dot(itemFeatures);
            topKItems.offer(new GenericRecommendedItem(itemID, (float) predictedRating));
          }
          return true;
        }
      });

      List<RecommendedItem> recommendedItems = Lists.newArrayListWithExpectedSize(recommendationsPerUser);
      for (RecommendedItem topItem : topKItems.retrieve()) {
        recommendedItems.add(new GenericRecommendedItem(topItem.getItemID(), Math.min(topItem.getValue(), maxRating)));
      }

      if (!topKItems.isEmpty()) {
        ctx.write(userIDWritable, new RecommendedItemsWritable(recommendedItems));
      }
    }

先說下這個輸入格式吧:<key,vlaue>  -->   <userid,[itemid:rating,itemid,rating,...],針對當前的輸入,首先獲取用戶userid和這個用戶全部評價過的item(放在alreadyRatedItems)   。而後遍歷全部M中的item若是這個item不存在於alreadyRatedItems那麼就對這個item進行評分預測,預測使用的仍是在U中和M中取出分別對應userid和itemid的行、列向量進行點乘便可獲得預測分數,而後把這個預測的分數加入到topKItems中,看下這個變量的定義:源碼分析

 

 

final TopK<RecommendedItem> topKItems = new TopK<RecommendedItem>(recommendationsPerUser, BY_PREFERENCE_VALUE);

這裏的BY_PREFERENCE_VALUE實現了comparator接口,因此加入到這個變量中的會按照必定的順序來排列,即按照value(即預測的分數)從大到小來排列。而後就是輸出了。這樣這個系列就所有分析完畢了。lua

 

額,還有一點,關於matlab代碼部分的算法實際上是和mahout源碼中的一致的,具體下篇分析。spa


 

分享,成長,快樂

轉載請註明blog地址:http://blog.csdn.net/fansy1990

相關文章
相關標籤/搜索