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

相关文章
相关标签/搜索