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

经过了SimilarityJob的计算共生矩阵后,就可以开始下面一个过程了,这个过程主要是共生矩阵的乘法,要说这个共生矩阵的乘法是啥意思?我也不是很清楚,不清楚就看代码呗。

首先明确共生矩阵,即共生矩阵的输入文件(也是上面个SimilarityJob的输出文件):

  1. similarityMatrix=================
  2. {102={101:0.14201473202245876,106:0.14972506706560876,105:0.14328432723886902,104:0.12789210656028413,103:0.1975496259559987},
  3. 103={101:0.15548737703860027,106:0.1424339656566283,105:0.11208890297777215,104:0.14037600977966974,102:0.1975496259559987},
  4. 101={107:0.10275248635596666,106:0.1424339656566283,105:0.1158457425543559,104:0.16015261286229274,103:0.15548737703860027,102:0.14201473202245876},
  5. 106={101:0.1424339656566283,105:0.14201473202245876,104:0.18181818181818182,103:0.1424339656566283,102:0.14972506706560876},
  6. 107={105:0.2204812092115424,104:0.13472338607037426,101:0.10275248635596666},
  7. 104={107:0.13472338607037426,106:0.18181818181818182,105:0.16736577623297264,103:0.14037600977966974,102:0.12789210656028413,101:0.16015261286229274},
  8. 105={107:0.2204812092115424,106:0.14201473202245876,104:0.16736577623297264,103:0.11208890297777215,102:0.14328432723886902,101:0.1158457425543559}}

计算共生矩阵一共分为三个job,下面来分别看:

(1)prePartialMultiply1:
这个job的调用代码如下:

  1. Job prePartialMultiply1 = prepareJob(
  2. similarityMatrixPath, prePartialMultiplyPath1, SequenceFileInputFormat.class,
  3. SimilarityMatrixRowWrapperMapper.class, VarIntWritable.class, VectorOrPrefWritable.class,
  4. Reducer.class, VarIntWritable.class, VectorOrPrefWritable.class,
  5. SequenceFileOutputFormat.class);

可以看到其实只是有一个mapper而已,reducer形同虚设,那就只看mapper吧

(1.1)mapper://SimilarityMatrixRowWrapperMapper

这个mapper同样很简单,只有一个map函数,如下:

(1.1.2)map:

  1. protected void map(IntWritable key,
  2. VectorWritable value,
  3. Context context) throws IOException, InterruptedException {
  4. Vector similarityMatrixRow = value.get();
  5. /* remove self similarity */
  6. similarityMatrixRow.set(key.get(), Double.NaN);
  7. context.write(new VarIntWritable(key.get()), new VectorOrPrefWritable(similarityMatrixRow));
  8. }

map中首先获得输入的value,就是和项目key不同的其他项目的相似度向量;然后是把自身的相似度设置为最大,在前面similarityJob中最后是把自身的相似度设置为0的,这里又设置为最大(前面设置为0,应该会在输出文件中就会相应的占少点空间,然后这里设置为最大值是为了后面计算的需要吧),然后就把key和value输出了,这里看到key和value都进行了类型转换,其中的key的类型变为了VarIntWritable应该只是为了速度的考虑吧(为啥之前不用?),而value的类型转为VectorOrPrefWritable应该是为了后面转换的考虑;

(1.2)reducer://Reducer

  1. protected void reduce(KEYIN key, Iterable<VALUEIN> values, Context context
  2. ) throws IOException, InterruptedException {
  3. for(VALUEIN value: values) {
  4. context.write((KEYOUT) key, (VALUEOUT) value);
  5. }
  6. }

等于就是把mapper中的重新输出一遍而已;

所以他的输出应该类似于:

  1. {102={106:0.1497250646352768,105:0.14328432083129883,104:0.12789210677146912,103:0.19754962623119354,102:NaN,101:0.14201472699642181},
  2. 103={106:0.14243397116661072,105:0.11208890378475189,104:0.140376016497612,103:NaN,102:0.19754962623119354,101:0.15548737347126007},
  3. 101={107:0.10275248438119888,106:0.14243397116661072,105:0.11584573984146118,104:0.1601526141166687,103:0.15548737347126007,102:0.14201472699642181,101:NaN},
  4. 106={106:NaN,105:0.14201472699642181,104:0.1818181872367859,103:0.14243397116661072,102:0.1497250646352768,101:0.14243397116661072},
  5. 107={101:0.10275248438119888,107:NaN,105:0.22048120200634003,104:0.13472338020801544}, 
  1. 104={107:0.13472338020801544,106:0.1818181872367859,105:0.16736577451229095,104:NaN,103:0.140376016497612,102:0.12789210677146912,101:0.1601526141166687}, 105={107:0.22048120200634003,106:0.14201472699642181,105:NaN,104:0.16736577451229095,103:0.11208890378475189,102:0.14328432083129883,101:0.11584573984146118}}

(2)prePartialMultiply2:

首先是其调用代码:

  1. Job prePartialMultiply2 = prepareJob(new Path(prepPath, PreparePreferenceMatrixJob.USER_VECTORS),
  2. prePartialMultiplyPath2, SequenceFileInputFormat.class, UserVectorSplitterMapper.class, VarIntWritable.class,
  3. VectorOrPrefWritable.class, Reducer.class, VarIntWritable.class, VectorOrPrefWritable.class,
  4. SequenceFileOutputFormat.class);
  5. if (usersFile != null) {
  6. prePartialMultiply2.getConfiguration().set(UserVectorSplitterMapper.USERS_FILE, usersFile);
  7. }
  8. prePartialMultiply2.getConfiguration().setInt(UserVectorSplitterMapper.MAX_PREFS_PER_USER_CONSIDERED,
  9. maxPrefsPerUser);

这里看到同样的也只是有mapper在起作用而已,reducer同样的形同虚设;然后就是可以提供一个usersFile文件用于过滤不关心的用户,这个在这里没有设置,默认是null的,同样后面的maxPrefsPerUser也没有设置,所以也是默认值;

首先来明确一下这里的输入文件,这里的输入是第一个PreparePreferenceMatrixJob中的userVecotr的输出,根据前面的分析,得到文件如下:

  1. {1={103:2.5,102:3.0,101:5.0},
  2. 2={101:2.0,104:2.0,103:5.0,102:2.5},
  3. 3={101:2.5,107:5.0,105:4.5,104:4.0},
  4. 4={101:5.0,106:4.0,104:4.5,103:3.0},
  5. 5={106:4.0,105:3.5,104:4.0,103:2.0,102:3.0,101:4.0}}

(2.1)mapper://UserVectorSplitterMapper

这个mapper是有setup和map函数的,下面一个个分析:

(2.1.1)setup:

  1. protected void setup(Context context) throws IOException {
  2. Configuration jobConf = context.getConfiguration();
  3. maxPrefsPerUserConsidered = jobConf.getInt(MAX_PREFS_PER_USER_CONSIDERED, DEFAULT_MAX_PREFS_PER_USER_CONSIDERED);
  4. String usersFilePathString = jobConf.get(USERS_FILE);
  5. if (usersFilePathString != null) {
  6. FSDataInputStream in = null;
  7. try {
  8. Path unqualifiedUsersFilePath = new Path(usersFilePathString);
  9. FileSystem fs = FileSystem.get(unqualifiedUsersFilePath.toUri(), jobConf);
  10. usersToRecommendFor = new FastIDSet();
  11. Path usersFilePath = unqualifiedUsersFilePath.makeQualified(fs);
  12. in = fs.open(usersFilePath);
  13. for (String line : new FileLineIterable(in)) {
  14. try {
  15. usersToRecommendFor.add(Long.parseLong(line));
  16. } catch (NumberFormatException nfe) {
  17. log.warn("usersFile line ignored: {}", line);
  18. }
  19. }
  20. } finally {
  21. Closeables.closeQuietly(in);
  22. }
  23. }
  24. }

汗,写了这么多代码,如果不用过滤用户的话,那么if里面的都没用了。不过,有时有一些特殊的需要(不关系其他用户,提高效率),这样写还是很不错的。其实看if里面做的事情也只是把文件中的user读出来,然后放在一个类似集合里面,然后在map中做过滤而已。除了if里面的代码,在setup中基本就没做其他事情了,因为那个maxPrefsPerUser没有设置,按默认的来,所以。。。

(2.1.2)map:

  1. protected void map(VarLongWritable key,
  2. VectorWritable value,
  3. Context context) throws IOException, InterruptedException {
  4. long userID = key.get();
  5. if (usersToRecommendFor != null && !usersToRecommendFor.contains(userID)) {
  6. return;
  7. }
  8. Vector userVector = maybePruneUserVector(value.get());
  9. Iterator<Vector.Element> it = userVector.iterateNonZero();
  10. VarIntWritable itemIndexWritable = new VarIntWritable();
  11. VectorOrPrefWritable vectorOrPref = new VectorOrPrefWritable();
  12. while (it.hasNext()) {
  13. Vector.Element e = it.next();
  14. itemIndexWritable.set(e.index());
  15. vectorOrPref.set(userID, (float) e.get());
  16. context.write(itemIndexWritable, vectorOrPref);
  17. }
  18. }

map的内容也不是很多,首先把userid读出来,然后在userToRecommendFor(就是在setup中读文件读到的user,这里没有而已)中过滤这个userid,如果没有就直接返回。接下来是对输入的value进行处理了,看下这个maybePruneUserVector函数是干嘛的:

  1. private Vector maybePruneUserVector(Vector userVector) {
  2. if (userVector.getNumNondefaultElements() <= maxPrefsPerUserConsidered) {
  3. return userVector;
  4. }
  5.  
  6. float smallestLargeValue = findSmallestLargeValue(userVector);
  7.  
  8. // "Blank out" small-sized prefs to reduce the amount of partial products
  9. // generated later. They're not zeroed, but NaN-ed, so they come through
  10. // and can be used to exclude these items from prefs.
  11. Iterator<Vector.Element> it = userVector.iterateNonZero();
  12. while (it.hasNext()) {
  13. Vector.Element e = it.next();
  14. float absValue = Math.abs((float) e.get());
  15. if (absValue < smallestLargeValue) {
  16. e.set(Float.NaN);
  17. }
  18. }
  19. return userVector;
  20. }

先看if条件判断,因为传入的vector最大的size也只是7(一共有7个项目),所以这里判断是true的,那么就直接返回了原来的vector了。但是现实中一般的项目数肯定是大于10的,所以,如果maxPrefsPerUser是按默认值的话,就会执行下面的代码,而不是直接返回原始向量,那么分析下吧。紧接着是一个findSmallestLargeValue函数:

  1. private float findSmallestLargeValue(Vector userVector) {
  2.  
  3. TopK<Float> topPrefValues = new TopK<Float>(maxPrefsPerUserConsidered, new Comparator<Float>() {
  4. @Override
  5. public int compare(Float one, Float two) {
  6. return one.compareTo(two);
  7. }
  8. });
  9.  
  10. Iterator<Vector.Element> it = userVector.iterateNonZero();
  11. while (it.hasNext()) {
  12. float absValue = Math.abs((float) it.next().get());
  13. topPrefValues.offer(absValue);
  14. }
  15. return topPrefValues.smallestGreat();
  16. }

目测这个函数应该是输出最小的float值,做个测试吧:

  1. package mahout.fansy.item;
  2.  
  3. import java.util.Comparator;
  4. import java.util.Iterator;
  5.  
  6. import org.apache.mahout.cf.taste.common.TopK;
  7. import org.apache.mahout.math.RandomAccessSparseVector;
  8. import org.apache.mahout.math.Vector;
  9.  
  10. import junit.framework.TestCase;
  11. /*
  12. * 测试findSmallestLargeValue方法;
  13. */
  14. public class TestTopK extends TestCase {
  15. private Vector v=null;
  16. public void setup(){
  17. v=new RandomAccessSparseVector(Integer.MAX_VALUE);
  18. //{103:2.5,102:3.0,101:5.0}
  19. v.set(101, 5.0);
  20. v.set(102,3.0);
  21. v.set(103,2.5);
  22. }
  23.  
  24. public void testTopK(){
  25. this.setup();
  26. TopK<Float> topPrefValues = new TopK<Float>(10, new Comparator<Float>() {
  27. @Override
  28. public int compare(Float one, Float two) {
  29. return one.compareTo(two);
  30. }
  31. });
  32.  
  33. Iterator<Vector.Element> it = v.iterateNonZero();
  34. while (it.hasNext()) {
  35. float absValue = Math.abs((float) it.next().get());
  36. topPrefValues.offer(absValue);
  37. }
  38. System.out.println("the value is:"+topPrefValues.smallestGreat());
  39. }
  40. }

由上面的输出结果2.5来看,的确是最小的值了;maybePruneUserVector中后面的代码就不是很理解了,既然都找出了最小值了,那么怎么还会有最小值呢?看他的英文解释好像是说要过滤一些什么输出似的,但是好像这样过滤不了吧?疑问???

如果真的是按照我想的那样的话,那么uservector还是输出原始值的;接下来的map中的代码和以前的分析中有些内容很相似,针对这样的输入<key,value>  -->  <userid,vector[itemid1:prefValue1,itemid2:prefValue2]> 输出为 <itemid1,vector[userid:prefValue1]>   、<itemid2,vecotr[userid:prefVlaue2]>,同样的,格式也是经过设置的。

(2.2)reducer://Reducer也就是把mapper的输出结果重复输出一遍,没有其他操作。

(3)partialMultiply:

其调用代码如下:

  1. Job partialMultiply = prepareJob(
  2. new Path(prePartialMultiplyPath1 + "," + prePartialMultiplyPath2), partialMultiplyPath,
  3. SequenceFileInputFormat.class, Mapper.class, VarIntWritable.class, VectorOrPrefWritable.class,
  4. ToVectorAndPrefReducer.class, VarIntWritable.class, VectorAndPrefsWritable.class,
  5. SequenceFileOutputFormat.class);
  6. setS3SafeCombinedInputPath(partialMultiply, getTempPath(), prePartialMultiplyPath1, prePartialMultiplyPath2);

先看下setS3SafeCombinedInputPath的操作是什么,

  1. public static void setS3SafeCombinedInputPath(Job job, Path referencePath, Path inputPathOne, Path inputPathTwo)
  2. throws IOException {
  3. FileSystem fs = FileSystem.get(referencePath.toUri(), job.getConfiguration());
  4. FileInputFormat.setInputPaths(job, inputPathOne.makeQualified(fs), inputPathTwo.makeQualified(fs));
  5. }

根据英文解释是说这样可以支持在Amazon S3上运行,因为MultipleInputs还没有投入使用。姑且认为这个是设置两个输入路径合法吧。

这个job有mapper也有reducer,额,好吧,这次该mapper形同虚设了,下面一个个分析:

先说下输入输入格式吧,输入数据有两种状态,(数据的类型是一样的):

其一,  <itemid, user:prefValue>; 其二,< itemid ,[itemid:prefValue,itemid:prefValue]>。

(3.1)mapper://Mapper,不解释。。。

(3.2)reducer://ToVectorAndPrefReducer

(3.2.1)reduce:

  1. protected void reduce(VarIntWritable key,
  2. Iterable<VectorOrPrefWritable> values,
  3. Context context) throws IOException, InterruptedException {
  4.  
  5. List<Long> userIDs = Lists.newArrayList();
  6. List<Float> prefValues = Lists.newArrayList();
  7. Vector similarityMatrixColumn = null;
  8. for (VectorOrPrefWritable value : values) {
  9. if (value.getVector() == null) {
  10. // Then this is a user-pref value
  11. userIDs.add(value.getUserID());
  12. prefValues.add(value.getValue());
  13. } else {
  14. // Then this is the column vector
  15. if (similarityMatrixColumn != null) {
  16. throw new IllegalStateException("Found two similarity-matrix columns for item index " + key.get());
  17. }
  18. similarityMatrixColumn = value.getVector();
  19. }
  20. }
  21.  
  22. if (similarityMatrixColumn == null) {
  23. return;
  24. }
  25.  
  26. VectorAndPrefsWritable vectorAndPrefs = new VectorAndPrefsWritable(similarityMatrixColumn, userIDs, prefValues);
  27. context.write(key, vectorAndPrefs);
  28. }

reduce操作其实就等于是把相同的key的所有属性放到一个变量中,比如item是101的vector为 101={107:0.10275248635596666,106:0.1424339656566283,105:0.1158457425543559,104:0.16015261286229274,103:0.15548737703860027,102:0.14201473202245876},即similarityMatrixColumn变量;然后userIDs就是101={5:4.0,4:5.0,3:2.5,2:2.0,1:5.0}其中的userids={5,4,3,2,1},prefValues={4.0,5.0,2.5,2.0,5.0}.

测试输入的结果如下:

  1. {102={106:0.1497250646352768,105:0.14328432083129883,104:0.12789210677146912,103:0.19754962623119354,102:NaN,101:0.14201472699642181} [5, 1, 2] [3.0, 3.0, 2.5],
  2. 103={106:0.14243397116661072,105:0.11208890378475189,104:0.140376016497612,103:NaN,102:0.19754962623119354,101:0.15548737347126007} [4, 1, 2, 5] [3.0, 2.5, 5.0, 2.0],
  3. 101={107:0.10275248438119888,106:0.14243397116661072,105:0.11584573984146118,104:0.1601526141166687,103:0.15548737347126007,102:0.14201472699642181,101:NaN} [5, 1, 4, 2, 3] [4.0, 5.0, 5.0, 2.0, 2.5],
  4. 106={106:NaN,105:0.14201472699642181,104:0.1818181872367859,103:0.14243397116661072,102:0.1497250646352768,101:0.14243397116661072} [4, 5] [4.0, 4.0], 
  1. 107={101:0.10275248438119888,107:NaN,105:0.22048120200634003,104:0.13472338020801544} [3] [5.0],
  2. 104={107:0.13472338020801544,106:0.1818181872367859,105:0.16736577451229095,104:NaN,103:0.140376016497612,102:0.12789210677146912,101:0.1601526141166687} [4, 2, 5, 3] [4.5, 2.0, 4.0, 4.0],
  3. 105={107:0.22048120200634003,106:0.14201472699642181,105:NaN,104:0.16736577451229095,103:0.11208890378475189,102:0.14328432083129883,101:0.11584573984146118} [5, 3] [3.5, 4.5]}

从上面的文件读取结果来看,分析思路是正确的。

分享,成长,快乐

转载请注明blog地址:http://blog.csdn.net/fansy1990

mahout算法源码分析之Itembased Collaborative Filtering(四)共生矩阵乘法的更多相关文章

  1. mahout算法源码分析之Itembased Collaborative Filtering(二)RowSimilarityJob

    Mahout版本:0.7,hadoop版本:1.0.4,jdk:1.7.0_25 64bit. 本篇开始之前先来验证前篇blog的分析结果,编写下面的测试文件来进行对上篇三个job的输出进行读取: p ...

  2. mahout算法源码分析之Itembased Collaborative Filtering(三)RowSimilarityJob验证

    Mahout版本:0.7,hadoop版本:1.0.4,jdk:1.7.0_25 64bit. 本篇分析上篇的分析是否正确,主要是编写上篇输出文件的读取以及添加log信息打印相关变量. 首先,编写下面 ...

  3. mahout算法源码分析之Collaborative Filtering with ALS-WR 并行思路

    Mahout版本:0.7,hadoop版本:1.0.4,jdk:1.7.0_25 64bit. mahout算法源码分析之Collaborative Filtering with ALS-WR 这个算 ...

  4. mahout算法源码分析之Collaborative Filtering with ALS-WR (四)评价和推荐

    Mahout版本:0.7,hadoop版本:1.0.4,jdk:1.7.0_25 64bit. 首先来总结一下 mahout算法源码分析之Collaborative Filtering with AL ...

  5. mahout算法源码分析之Collaborative Filtering with ALS-WR拓展篇

    Mahout版本:0.7,hadoop版本:1.0.4,jdk:1.7.0_25 64bit. 额,好吧,心头的一块石头总算是放下了.关于Collaborative Filtering with AL ...

  6. diff.js 列表对比算法 源码分析

    diff.js列表对比算法 源码分析 npm上的代码可以查看 (https://www.npmjs.com/package/list-diff2) 源码如下: /** * * @param {Arra ...

  7. 【Zookeeper】源码分析之请求处理链(四)

    一.前言 前面分析了SyncReqeustProcessor,接着分析请求处理链中最后的一个处理器FinalRequestProcessor. 二.FinalRequestProcessor源码分析 ...

  8. 【Zookeeper】源码分析之请求处理链(四)之FinalRequestProcessor

    一.前言 前面分析了SyncReqeustProcessor,接着分析请求处理链中最后的一个处理器FinalRequestProcessor. 二.FinalRequestProcessor源码分析 ...

  9. 精尽MyBatis源码分析 - SQL执行过程(四)之延迟加载

    该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...

随机推荐

  1. Android学习随笔--ListView的分页功能

    第一次写博客,可能格式,排版什么的会非常不美观,不过我主要是为了记录自己的Android学习之路,为了以后能有些东西回顾.既然是为了学习,那我肯定会吸收各位大大们的知道经验,有不足的地方请指出. 通过 ...

  2. PHP实现的Mysql读写分离

    本代码是从uchome的代码修改的,是因为要解决uchome的效率而处理的.这个思维其实很久就有了,只是一直没有去做,相信也有人有同样的想法,如果有类似的,那真的希望提出相关的建议.封装的方式比较简单 ...

  3. C++容器和算法

    转自:http://www.cnblogs.com/haiyupeter/archive/2012/07/29/2613145.html 容器:某一类型数据的集合. C++标准顺序容器包括:vecto ...

  4. linux下使用go-oci8

    地址:https://github.com/wendal/go-oci8 它是 https://github.com/mattn/go-oci8 的分支. win下安装步骤参考:http://www. ...

  5. A.xml

    pre{ line-height:1; color:#1e1e1e; background-color:#f0f0f0; font-size:16px;}.sysFunc{color:#627cf6; ...

  6. css3 --- 翻页动画 --- javascript --- 3d --- Action

    用css3和javascript做一个翻页动画<Action> 如有疑问请参照我的上一篇随笔:http://www.cnblogs.com/kodoyang/p/Html_Css3_Car ...

  7. php时间函数

    PHP中的时间函数有这么些:(1)date用法: date(格式,[时间]);如果没有时间参数,则使用当前时间. 格式是一个字符串,其中以下字符有特殊意义:U 替换成从一个起始时间(好象是1970年1 ...

  8. string中的substr() 和 find() 函数

    string问题中经常遇到在stringA中查找stringB,主要通过substr()跟find()来完成 substr().find().replace() 都可以用一个位置加上一个长读去描述子串 ...

  9. Emmet使用手册

    语法:   1.后代:> 缩写:nav>ul>li   < nav>     < ul>         < li></ li >   ...

  10. js和jquery实现tab选项卡

    <!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ...