使用Mahout搭建推荐系统之入门篇-Mahout实战

article/2025/10/9 20:59:11

原始地址:http://my.oschina.net/Cfreedom/blog/201828

用意: 结合上篇博客,写写代码熟悉一下Mahout。很多地方想法都比较粗糙,亟待指正。 
代码放在了:  https://github.com/xiaoqiangkx/qingRS

一、基本内容 

1. 加载数据: 判断userID和itemID的大小关系 
2. 过滤数据: 评分较少的用户直接过滤掉, 那些评分均一致且评分数量多的用户过滤掉. 计算过滤百分比, 如果过滤过多, 则需要考虑其它方法了. 
3. DataModel选择: 选择数据库存储还是文件存储; 选择GenericDataModel还是GenericBooleanDataModel 
4. 选择相似矩阵和参数, 如N值和门限值; 可视化(可选). 

二、运行环境 

JAVA MYSQL等配置参考"最美的词"  基于mahout的电影推荐系统
Mahout环境搭建 
本篇使用mahout 0.8的taste等相关jar包进行开发, jar包可以从   http://mirror.bit.edu.cn/apache/mahout/mahout-distribution-0.8.tar.gz中摘取,也可以在百度网盘上下载 http://pan.baidu.com/s/1iSOWk. 
与上次不同, 0.8版本的distribution合并了两个包, 上次漏了两个log包, 最终只需要引入7个包即可. 
mahout核心类不变: 提供推荐Model等核心类 
mahout-core-0.8.jar 
mahout-math-0.8.jar 
辅助类: 提供Log和部分数学公式类. 
slf4j-api-1.7.5.jar commons-logging-1.1.1.jar slf4j-jcl-1.7.5.jar提供Log服务 
guava-14.1.0.jar合并了两个google相关的数学类google-collections.jar和guava.jar 
commons-math3-3.2.jar包取代了uncommons-maths-1.2.jar类    

三、程序运行 

搭建基本框架并进行简单测试 
我在博文1的框架下做了一点小改动, 从而说明推荐算法算法的结果不稳定性以及调参的重要性. 推荐系统不像一般的业务逻辑, 搭建好系统只完成了极小的一部分, 重点在于调参和响应速度.  
类似于博客1中叙述所述, 搭建基本的框架, 并引入movielens 100K中的u.data数据,运行成功.  
工程目录结构: 
 

[数据格式说明: movielens u.data数据格式为"244     51     2 880606923", 以tab隔开. 表示ID为244的用户对ID为51的物品打分为2分, 时间为880606923, 猜测类似于从1970年1月1日开始记的秒数, 数量级差不多, 暂时不使用此参数.] 

首先介绍User-based和Item-based的方法. 
以User-based为例, 将每一个物品表示为一个维度, 那么每个用户都可以表示为一个向量. 如果一个有{101, 102, 103, 104, 105}五个物品, 用户1对101评分为2.0, 对105评分为3.0, 那么用户1可以表示为[2.0, 0, 0, 0, 3.0]. 那么用户之间就有距离, 距离由Similarity相似性决定, 常见的如欧拉距离. 如果我们确定了所有用户间的距离, 那么可以使用N近邻法或者门限法确定每个人的相邻圈子, 如下所示.  

如何选择每个item或者user响铃圈子: 
常见的有N近邻法和门限值法. 如下面2图所示: 
 
此图表示N = 3时,选择与1最近的前三位2, 4, 5而排除3.  1的圈子由2, 4, 5组成. 
 
此图表示门限(Threshold)选择法, 4, 5 在门限之内, 而2. 3在门限之外. 1的圈子由4, 5组成. 
总结: 那么接下来的问题就是如何定义相似性, 即计算距离了. 

3.1 调整N值和Threshold值对推荐结果的影响: 

重要代码片段如下:  
  1. public static void main(String[] args) throws Exception {
  2. int userId = 1;
  3. int rankNum = 2;
  4. QingRS qingRS = new QingRS();
  5. for(int neighberNum = 2; neighberNum < 10; neighberNum++) {
  6. System.out.println("neigherNum=" + neighberNum);
  7. qingRS.initRecommenderIntro(filename, neighberNum);
  8. String resultStr = qingRS.getRecommender(userId, rankNum);
  9. System.out.println(resultStr);
  10. }
  11. }


运行结果:
A. 当neigherhood从2到9变化时, 推荐的物品前期在变化, 后期趋于稳定. 
neigherNum=2 

Recommend=313 4.5
neigherNum=3
Recommend=286 5.0
neigherNum=4
Recommend=286 5.0
neigherNum=5
Recommend=990 5.0
neigherNum=6
Recommend=990 5.0
neigherNum=7
Recommend=990 5.0
neigherNum=8
Recommend=990 5.0
neigherNum=9
Recommend=990 5.0 

解释: neigherhood一开始变化时, 参考的人数增多了, 所谓三个臭皮匠顶过一个诸葛亮, 推荐将会变化, 但是随着neigherhood的变大, 加再多的人进来也只是凑人数而已没有多大的决定能力. 

B. 当rankNum从2到10变化时, 感觉上rankNum的改变不应该影响推荐结果. 

List<RecommendedItem> recommendations = recommender.recommend(userid,
rankNum); 

但是: 我们发现除了neigherNum = 2以外, 推荐结果均发生了变化, 而且数据开始震荡, 如果将neigherNum放大到30, 推荐结果依旧不停地震荡.  

neigherNum=2 

Recommend=313 4.5
neigherNum=3
Recommend=323 5.0
neigherNum=4
Recommend=898 5.0
neigherNum=5
Recommend=323 5.0
neigherNum=6
Recommend=323 5.0
neigherNum=7
Recommend=898 5.0
neigherNum=8
Recommend=326 5.0
neigherNum=9
Recommend=326 5.0 

解释???: 问题应该出在排序算法上, Mahout为了节约内存使用了qSort, 因此排序算法不稳定. 但是我去查看Mahout源代码发现GenericUserBasedRecommender中使用了Collections.sort(), sort默认使用的是MergeSort, 所以排序应该是稳定的. 依旧存在着疑问. 

3.2. 针对DataModel做一些数据分析,  

类似于博文2, 判断item和user数量, value范围, 方差等. 

代码如下: 

  1. package com.qingfeng.rs.test;
  2. import java.io.File;
  3. import java.io.IOException;
  4. import org.apache.mahout.cf.taste.common.TasteException;
  5. import org.apache.mahout.cf.taste.impl.common.LongPrimitiveIterator;
  6. import org.apache.mahout.cf.taste.impl.model.file.FileDataModel;
  7. import org.apache.mahout.cf.taste.model.DataModel;
  8. public class QingDataModelTest {
  9. private final static String filename = "data/u.data";
  10. public static void main(String[] args) throws IOException, TasteException {
  11. DataModel dataModel = new FileDataModel(new File(filename));
  12. // compute the max and min value
  13. // 计算最大最小值
  14. float maxValue = dataModel.getMaxPreference();
  15. float minValue = dataModel.getMinPreference();
  16. // compute the number of usersNum and itemsNum
  17. // 计算用户和物品总数
  18. int usersNum = dataModel.getNumUsers();
  19. int itemsNum = dataModel.getNumItems();
  20. int[] itemsNumForUsers = new int[usersNum];
  21. int[] usersNumForItems = new int[itemsNum];
  22. LongPrimitiveIterator userIDs = dataModel.getUserIDs();
  23. int i = 0;
  24. while (userIDs.hasNext()) {
  25. itemsNumForUsers[i++] = dataModel.getPreferencesFromUser(
  26. userIDs.next()).length();
  27. }
  28. assert (i == usersNum);
  29. LongPrimitiveIterator itemIDs = dataModel.getItemIDs();
  30. i = 0;
  31. while (itemIDs.hasNext()) {
  32. usersNumForItems[i++] = dataModel.getPreferencesForItem(
  33. itemIDs.next()).length();
  34. }
  35. assert (i == itemsNum);
  36. // compute mean and variance
  37. // 计算平均值和方差
  38. double usersMean;
  39. double usersVar;
  40. int sum = 0;
  41. int sqSum = 0;
  42. for (int num : itemsNumForUsers) {
  43. sum += num;
  44. sqSum += num * num;
  45. }
  46. usersMean = (double) sum / usersNum;
  47. double userSqMean = (double) sqSum / usersNum;
  48. usersVar = Math.sqrt(userSqMean - usersMean * usersMean);
  49. double itemsMean;
  50. double itemsVar;
  51. sum = 0;
  52. sqSum = 0;
  53. for (int num : usersNumForItems) {
  54. sum += num;
  55. sqSum += num * num;
  56. }
  57. itemsMean = (double) sum / itemsNum;
  58. double itemsSqMean = (double) sqSum / itemsNum;
  59. itemsVar = Math.sqrt(itemsSqMean - itemsMean * itemsMean);
  60. System.out.println("Preference=(" + minValue + ", " + maxValue + ")");
  61. System.out.println("usersNum=" + usersNum + ", userMean=" + usersMean
  62. + ", userVar=" + usersVar);
  63. System.out.println("itemsNum=" + itemsNum + ", itemsMean=" + itemsMean
  64. + ", itemsVar=" + itemsVar);
  65. }
  66. }


设置门限过滤数据

在代码中加入过滤模块 
  1. for (int num : itemsNumForUsers) {
  2. sum += num;
  3. if (num < 20) {
  4. countLower++;
  5. // System.out.println("user warning(" + countLower + ")=" + num);
  6. }
  7. sqSum += num * num;
  8. }
  9. System.out.println("user warning(" + countLower + ")");
  10. for (int num : usersNumForItems) {
  11. sum += num;
  12. if (num < 20) {
  13. countLower++;
  14. //System.out.println("item warning(" + countLower + ")=" + num);
  15. }
  16. sqSum += num * num;
  17. }
  18. System.out.println("item warning(" + countLower + ")");


运行结果如下 
user warning(0) 
item warning(743) 
Preference=(1.0, 5.0) 
usersNum=943, userMean=106.04453870625663, userVar=100.87821227051644 
itemsNum=1682, itemsMean=59.45303210463734, itemsVar=80.3599467406018 

分析:与官方的1000个用户, 1700部电影的说法一致.   http://www.grouplens.org/datasets/movielens/
user warning(0) 
item warning(743) 表示有743个item评分个数小于20. 

     物品评分较为稀疏程度和物品总数大小是一致的. 使用user-based则用户少,节约内存, 且矩阵致密。 

设置门限为20时, 发现物品矩阵稀疏、方差大和过滤器的统计结果item warning(743)大是一致, 此处先不过滤数据, 后期再说. 

注:当然优秀的过滤器需要改变门限值来不停的调试 


3.3 选择DataModel, 并计算内存使用情况 

由于数据有rate, 所以不使用Boolean形式的存储.  
预估内存开销: 
由上文分析可知: Preference ~= usersNum * userMean ~= 100K, 每个Preference消耗28bytes, 
预估内存开销= 28bytes * 100K = 2.8 Mbytes. 此外相似矩阵如果使用邻接矩阵方式存储, max{usersNum, itemsNum}**2 * 4bytes(float) = 8Mbytes左右. 因此内存总结开销在10M左右. 
[但是查看Mahout源代码org.apache.mahout.cf.taste.impl中相关文件发现, 相似矩阵是临时计算的, 每次recommend时通过重写Estimator接口的estimate方法来具体实现. 可以mahout还是考虑到内存开销, 牺牲了计算速度吧. 所以估计程序运行内存开销依旧在2.8Mbytes左右. 究竟哪个是正确的理解呢?] 

因此我使用in-memory形式的GenericDataModel将数据直接加载到内存中.  

实验测试内存开销: 
通过多次调用System.gc()来回收内存, 通过Rumtime.totalMemory和Runtime.freeMemory()查看内存使用状态. 
http://docs.oracle.com/javase/6/docs/api/
代码如下:  
  1. public class QingMemoryTest {
  2. private static final String filename = "data/u.data";
  3. public static void main(String[] args) throws Exception {
  4. DataModel dataModel = new FileDataModel(new File(filename));
  5. UserSimilarity similarity = new PearsonCorrelationSimilarity(dataModel);
  6. UserNeighborhood neighborhood = new NearestNUserNeighborhood(5,
  7. similarity, dataModel);
  8. Recommender recommender = new GenericUserBasedRecommender(dataModel,
  9. neighborhood, similarity);
  10. System.out.println("1: jvm free-memory= "
  11. + Runtime.getRuntime().freeMemory() + "Bytes");
  12. System.gc();
  13. System.out.println("2: jvm free-memory= "
  14. + Runtime.getRuntime().freeMemory() + "Bytes");
  15. // dataModel被回收, 所以推荐结果错误.
  16. System.out.println(recommender.recommend(1, 2).get(1).getValue());
  17. }
  18. }


运行结果如下: 
start: jvm used-memory= 0.5967178344726562MB 

after dataModel: jvm used-memory= 19.2872314453125MB
after similarity: jvm used-memory= 19.2872314453125MB
after neighborhood: jvm used-memory= 19.58240509033203MB
after recommender: jvm used-memory= 19.58240509033203MB
recommend=340
after recommend first: jvm used-memory= 19.877883911132812MB
after gc: jvm used-memory= 9.829483032226562MB
recommend=340
after recommend second: jvm used-memory= 9.829483032226562MB 

分析: 由上述数据可见,gc回收内存后, JVM内存消耗回收了10Mbytes, 与猜测一致. 

问题: 回收完数据后, 为什么recommender还可以进行推荐, 而且没有额外的内存开销??? 

数据增长10倍, 即使用1M数据进行测试 

简单统计分析结果: 

user warning(0)
item warning(663)
Preference=(1.0, 5.0)
usersNum=6040, userMean=165.5975165562914, userVar=192.73107252940773
itemsNum=3706, itemsMean=269.88909875876953, itemsVar=383.9960197430679 

估计内存消耗: usersNum和itemsNum增长了3到6倍, 而相似矩阵消耗内存为平方级别, 那么内存消耗上线为9到36倍; 此外数据增长10倍, DataModel内存消耗为线性增长, 增长10倍内存消耗. 那么估计内存消耗= 2.8M * 10 + (9~36)*8M = 100M ~ 316M内存之间. 如果不存储相似矩阵, 那么内存消耗为28M左右.  

由于数据以"::"作为分割符, 用python简单处理一下,替换为\t 

  1. f = open("result.dat", "w")
  2. for line in open("ratings.dat", "r"):
  3. newLine = line.replace("::", "\t")
  4. f.write(newLine)

运行结果如下 

start: jvm used-memory= 0.5967178344726562MB 

after dataModel: jvm used-memory= 204.9770050048828MB
after similarity: jvm used-memory= 204.9770050048828MB
after neighborhood: jvm used-memory= 204.9770050048828MB
after recommender: jvm used-memory= 204.9770050048828MB
recommend=2908
after recommend first: jvm used-memory= 208.10643768310547MB
after gc: jvm used-memory= 76.12030029296875MB
recommend=2908
after recommend second: jvm used-memory= 76.12030029296875MB 

分析: 由上述数据可以: 数据回收了132Mbytes, 76M为运行开销. 与估计内存消耗移植. DataModel线性增长, 相似矩阵平方级别增长. 

结论: 如果评分数增加到10M级别, 用户或者物品数增长3~10倍, 那么需要4G到40G的内存才能快速的计算出推荐结果, 需要增加内存条, 设置JVM配置以及使用hadoop来实现. 另外真实的数据用户数达到GB级别, 总数达到TB级别, 需要的内存数量和运算量是十分恐怖的. 传统地算法已经无法满足要求, 需要借助Hadoop这种分布式来实现运算. 

     当然内存不够大, 硬盘可以很大, 处理10M级别以上的推荐数据时, 选择使用MysqlJDBCDataModel来实现存储.      

另外: 据数盟的一位Q友说, "淘宝有8kw的商品(记忆也许有出入),用户2亿,多大的矩阵啊". 每次想到这里, 都会默默地闭上双眼, 遥想远方的宇宙, 数据又是多么地浩淼. 在上帝眼中, 我们也许还只是玩过家家, 学1+1的小孩子吧.   

3.4. 选择相似性矩阵和调参 

此外, 后期希望考虑user-based, item-based, slope-one算法的比较, 同时参考运行时间. 

相似矩阵选择下面4种 

PearsonCorrelationSimilarity   EuclideanDistanceSimilarity  TanimotoCoefficientSimilarity  LogLikeLihoodSimilarity 

注:其中EuclideanDistanceSimilarity比较特殊, 它没有实现UserSimilarity接口, 所以不能放到一个Collection<UserSimilarity>容器中 

[注: 勿看了org.apache.mahout.math.hadoop.similarity.cooccurrence.measures文件] 

参数调整只选择近邻N和threashold 

这里给出代码原型, 但是在普通PC上跑100K的数据集都太慢了, 使用intro.csv这个toy数据跑一跑. 

N选择[2, 4, 8, ... 64], Threshold选择[0.9, 0.85, ... 0.7];  

代码如下: 

  1. public class QingParaTest {
  2. private final String filename = "data/intro.csv";
  3. private double threshold = 0.95;
  4. private int neighborNum = 2;
  5. private ArrayList<UserSimilarity> userSims;
  6. private final int SIM_NUM = 4;
  7. private final int NEIGHBOR_NUM = 64;
  8. private final double THRESHOLD_LOW = 0.7;
  9. public static void main(String[] args) throws IOException, TasteException {
  10. new QingParaTest().valuate();
  11. }
  12. public QingParaTest() {
  13. super();
  14. this.userSims = new ArrayList<UserSimilarity>();
  15. }
  16. private void valuate() throws IOException, TasteException {
  17. DataModel dataModel = new FileDataModel(new File(filename));
  18. RecommenderEvaluator evaluator = new AverageAbsoluteDifferenceRecommenderEvaluator();
  19. // populate Similarity
  20. populateUserSims(dataModel);
  21. int simBest = -1;
  22. double scoreBest = 5.0;
  23. int neighborBest = -1;
  24. double thresholdBest = -1;
  25. System.out.println("SIM\tNeighborNum\t\tThreshold\tscore");
  26. for (int i = 0; i < SIM_NUM; i++) {
  27. for (neighborNum = 2; neighborNum <= NEIGHBOR_NUM; neighborNum *= 2) {
  28. for (threshold = 0.75; threshold >= THRESHOLD_LOW; threshold -= 0.05) {
  29. double score = 5.0;
  30. QingRecommenderBuilder qRcommenderBuilder = new QingRecommenderBuilder(
  31. userSims.get(i), neighborNum, threshold);
  32. // Use 70% of the data to train; test using the other 30%.
  33. score = evaluator.evaluate(qRcommenderBuilder, null,
  34. dataModel, 0.7, 1.0);
  35. System.out.println((i + 1) + "\t" + neighborNum + "\t"
  36. + threshold + "\t" + score);
  37. if (score < scoreBest) {
  38. scoreBest = score;
  39. simBest = i + 1;
  40. neighborBest = neighborNum;
  41. thresholdBest = threshold;
  42. }
  43. }
  44. }
  45. }
  46. System.out.println("The best parameter");
  47. System.out.println(simBest + "\t" + neighborBest + "\t" + thresholdBest
  48. + "\t" + scoreBest);
  49. }
  50. private void populateUserSims(DataModel dataModel) throws TasteException {
  51. UserSimilarity userSimilarity = new PearsonCorrelationSimilarity(
  52. dataModel);
  53. userSims.add(userSimilarity);
  54. userSimilarity = new TanimotoCoefficientSimilarity(dataModel);
  55. userSims.add(userSimilarity);
  56. userSimilarity = new LogLikelihoodSimilarity(dataModel);
  57. userSims.add(userSimilarity);
  58. userSimilarity = new EuclideanDistanceSimilarity(dataModel);
  59. userSims.add(userSimilarity);
  60. }
  61. }
  62. class QingRecommenderBuilder implements RecommenderBuilder {
  63. private UserSimilarity userSimilarity;
  64. private int neighborNum;
  65. private double threshold;
  66. public QingRecommenderBuilder(UserSimilarity userSimilarity,
  67. int neighborNum, double threshold) {
  68. super();
  69. this.userSimilarity = userSimilarity;
  70. this.neighborNum = neighborNum;
  71. this.threshold = threshold;
  72. }
  73. @Override
  74. public Recommender buildRecommender(DataModel dataModel)
  75. throws TasteException {
  76. UserNeighborhood neighborhood = new NearestNUserNeighborhood(
  77. neighborNum, threshold, userSimilarity, dataModel);
  78. return new GenericUserBasedRecommender(dataModel, neighborhood,
  79. userSimilarity);
  80. }
  81. }


运行结果如下: 

SIM NeighborNum Threshold score
1 2 0.75 0.4858379364013672
1 2 0.7 NaN
1 4 0.75 0.4676065444946289
1 4 0.7 NaN
1 8 0.75 0.8704338073730469
1 8 0.7 0.014162302017211914
1 16 0.75 NaN
1 16 0.7 0.7338032722473145
1 32 0.75 0.7338032722473145
1 32 0.7 0.4858379364013672
1 64 0.75 NaN
1 64 0.7 1.0 

The best parameter 

1 8 0.7 0.014162302017211914 

分析: 运行最佳的结果为N = 8, Threshold = 0.7 当然, 这个方法, 十分的粗糙, 但是也说明了参数的重要性, 毕竟推荐系统上线了必须有优秀的A\B Test结果, 要不然还不如使用打折, 优惠券来的简单实在. 

顺便截一张Mahout in Action上一个真实案例的数据, 如下图所示 

 
item-based与user_based一致, 基本上就是就Similarity, Neighborhood和Recommender的User换成Item即可. 

3.5 slope-one 

  1. public class SlopeOne {
  2. public static void main(String[] args) throws IOException, TasteException {
  3. DataModel dataModel = new FileDataModel(new File("data/intro.csv"));
  4. RecommenderEvaluator evaluator = new AverageAbsoluteDifferenceRecommenderEvaluator();
  5. double score = evaluator.evaluate(new SlopeOneNoWeighting(), null,
  6. dataModel, 0.7, 1.0);
  7. System.out.println(score);
  8. }
  9. }
  10. class SlopeOneNoWeighting implements RecommenderBuilder {
  11. public Recommender buildRecommender(DataModel model) throws TasteException {
  12. DiffStorage diffStorage = new MemoryDiffStorage(model,
  13. Weighting.UNWEIGHTED, Long.MAX_VALUE);
  14. return new SlopeOneRecommender(model, Weighting.UNWEIGHTED,
  15. Weighting.UNWEIGHTED, diffStorage);
  16. }
  17. }


运行结果为: 1.3571428571428572 当然这个结果意义不大, 因为数据集很小.  

四、总结 

推荐系统的难点在于各种参数、算法的选择,以及推荐系统整体架构的测试;如果希望搭建商业级别的应用,在数据和架构上所花的时间要比算法调参多一些。 

五、Similarity和Algorithm相关总结 

如何计算相似性: 
常见的方法如下表所示: Similarity只是描述计算方法, 并不计算并保存相似矩阵. 
相似性的基本思路就是不适用欧式距离的, 都得加上权重或者门限来防止交集较小的相似距离. 
相似距离(距离越小值越大)
优点 缺点 取值范围 
PearsonCorrelation 
类似于计算两个矩阵的协方差 
不受用户评分偏高 
或者偏低习惯影响的影响 
1. 如果两个item相似个数小于2时 
无法计算相似距离. 
[可以使用item相似个数门限来解决.] 
没有考虑两个用户之间的交集大小[使用weight参数来解决] 
2. 无法计算两个完全相同的items 
[-1, 1] 
EuclideanDistanceSimilarity 
计算欧氏距离, 使用1/(1+d) 
使用与评分大小较 
重要的场合 
如果评分不重要则需要归一化, 
计算量大 
同时每次有数据更新时麻烦 
[-1, 1]  
CosineMeasureSimilarity 
计算角度 
与PearsonCorrelation一致 
[-1, 1] 
SpearmanCorrelationSimilarity 
使用ranking来取代评分的 
PearsonCorrelation 
完全依赖评分和完全放弃评分之间的平衡 
计算rank消耗时间过大 
不利于数据更新 
[-1, 1] 
CacheUserSimilarity 
保存了一些tag, reference 
缓存经常查询的user-similarity 额外的内存开销 
TanimotoCoefficientSimilarity 
统计两个向量的交集占并集的比例 
同时并集个数越多, 越相近. 
适合只有相关性 
而没有评分的情况 
没有考虑评分,信息丢失了 [-1,1] 
LogLikeLihoodSimilarity 
是TanimoteCoefficientSimilarity 
的一种基于概率论改进 
计算两者重合的偶然性 
考虑了两个item相邻的独特性 
计算复杂 [-1,1] 

如何选择推荐算法:     
user-based算法: 最古老的算法, 计算相似的人群, 最大的问题是存储相似矩阵, 由于每个用户喜欢的物品在变化, 导致相似矩阵不停的变化. 更新相似矩阵计算量可能较大. 针对搜索引擎来说, 搜索词如果比用户数目多的话,可以考虑user-based.  
item-based算法: 与user-based类似, 每个物品被喜欢的用户个数不停地变化, 相似矩阵持续地更新. 在互联网时代,商品上百万, 用户上亿. 那么使用item-based比较靠谱, 物品相似矩阵变化较小, Amazon的推荐算法就是使用item-based为基础的. 
SVD: 现在比较流行的算法, 因为可以进行降维. 发掘有价值的特征维度来取代用户维度或者商品维度. 举个例子: 例如两个人分别喜欢保时捷和法拉利, user-based和item-based计算的相似性都很低, 但是SVD引入跑车或者奢侈品这种潜在的特征后, 两者就有相似性了. 当然缺点在于, SVD需要将整个矩阵加载到内存进行矩阵分解, 对内存消耗大, 不知道SVD的矩阵分解有没有Map-Reduce实现方法.  
Slope-One算法: 上述三种算法都不太适合作为在线算法和更新数据, 但是Slope-One可以. 举个例子, 假设所有用户评价电影A比电影B高1.0分, 评价电影C和电影A一致. 如果一个用户评价电影B为2.0分, 评价电影C为4.0分, 那么用户评价电影A为3.0分或者4.0分, 最佳的方法的取两者的加权平均值, 权重由同时出现次数决定. Slope-One可以离线计算所有的n*(n-1)/2中相关性, 当一个用户更新了电影时, 相关性更新快捷; 通过遍历一遍电影即可获得所有电影的评分,从而排序给出推荐. 缺点是相关性计算复杂. [个人觉得这个计算量也不小, 取决于电影个数以及用户评分电影个数] 

六、参考资料 


[1] Sean Owen "Mahout in Action"   http://book.douban.com/subject/4893547/


http://chatgpt.dhexx.cn/article/J4C166Zq.shtml

相关文章

Mahout初探

mahout官网,执行下面命令,详细安装过程可参考Mahout介绍、安装与应用案例 tar -xzvf apache-mahout-distribution-0.13.0.tar.gzln -s apache-mahout-distribution-0.13.0 mahout 修改profile环境变量 export MAHOUT_HOME/home/hadoop/application/mahout export MAHOUT_CONF…

mahout java api_Mahout推荐算法API详解

前言 用Mahout来构建推荐系统&#xff0c;是一件既简单又困难的事情。简单是因为Mahout完整地封装了“协同过滤”算法&#xff0c;并实现了并行化&#xff0c;提供非常简单的API接口&#xff1b;困难是因为我们不了解算法细节&#xff0c;很难去根据业务的场景进行算法配置和调…

mahout LDA

什么是Mahout&#xff1f; ” Apache Mahout™ project’s goal is to build a scalable machine learning library ” 我来拓展一下&#xff1a;(1) Mahout 是Apache旗下的开源项目&#xff0c;集成了大量的机器学习算法。(2) 大部分算法&#xff0c;可以运行在Hadoop上&…

Mahout

1、Mahout是什么 Mahout是一个算法库,集成了很多算法。Apache Mahout 是 Apache Software Foundation&#xff08;ASF&#xff09;旗下的一个开源项目&#xff0c;提供一些可扩展的机器学习领域经典算法的实现&#xff0c;旨在帮助开发人员更加方便快捷地创建智能应用程序。Ma…

mahout 详解

前言 用Mahout来构建推荐系统&#xff0c;是一件既简单又困难的事情。简单是因为Mahout完整地封装了“协同过滤”算法&#xff0c;并实现了并行化&#xff0c;提供非常简单的API接口&#xff1b;困难是因为我们不了解算法细节&#xff0c;很难去根据业务的场景进行算法配置和调…

基于Mahout实现协同过滤推荐算法的电影推荐系统

1 Mahout介绍 Apache Mahout 是 Apache Software Foundation&#xff08;ASF&#xff09; 旗下的一个开源项目&#xff0c;提供一些可扩展的机器学习领域经典算法的实现&#xff0c;旨在帮助开发人员更加方便快捷地创建智能应用程序。经典算法包括聚类、分类、协同过滤、进化编…

Mahout简介

Mahout简介 一、mahout是什么 Apache Mahout是ApacheSoftware Foundation &#xff08;ASF&#xff09;旗下的一个开源项目&#xff0c;提供了一些经典的机器学习的算法&#xff0c;皆在帮助开发人员更加方便快捷地创建智能应用程序。目前已经有了三个公共发型版本&#xff0…

推荐系统 Mahout入门之简单使用

Mahout&#xff1a; Apache Mahout 是 Apache Software Foundation&#xff08;ASF&#xff09;旗下的一个开源项目&#xff0c;提供一些可扩展的机器学习领域经典算法的实现&#xff0c;旨在帮助开发人员更加方便快捷地创建智能应用程序。Mahout项目目前已经有了多个公共发行…

Mahout介绍和简单应用

Mahout学习&#xff08;主要学习内容是Mahout中推荐部分的ItemCF、UserCF、Hadoop集群部署运行&#xff09; 1.Mahout是什么&#xff1f; Mahout是一个算法库,集成了很多算法。 Apache Mahout 是 Apache Software Foundation&#xff08;ASF&#xff09;旗下的一个开源项目&…

脚手架开发流程

先把原理讲通&#xff0c;方便后续的开发。 后续都拿vue-cli举例 脚手架实现原理 为什么全局安装vue/cli后会添加的命令为vue&#xff1f;全局安装vue/cli时发生了什么&#xff1f;执行vue命令时发生了什么&#xff1f;为什么vue指向一个js文件&#xff0c;我们却可以直接通过…

ns2模拟仿真实验

内容&#xff1a; 编写TCL脚本&#xff0c;搭建如下图所示的一个网络&#xff0c;共6个节点&#xff0c;其中2、3节点用做ftp服务器和客户端&#xff0c;4、5节点用做cbr流量的源和目的&#xff0c;而0、1节点用做转发设备。各节点间的链路属性见图。 模拟时间设为13秒钟&#…

NS2网络仿真环境的搭建和使用

一实验概述: 1学会安装和使用NS2&#xff1b;熟悉NS2的文档结构&#xff1b;掌握NS2的仿真环境、使用界面和操作方法。 2学会安装NS2仿真环境和配置。 3了解NS2的工作原理和程序组成。 4熟悉NS2中的脚本语言Tcl和Otcl 5学习分裂对象模型和TclCL 6NS的时间调度机制学习。 …

NS2的NODE类——node

本文转自&#xff1a;http://hi.baidu.com/wirelesscat/blog/item/67c6db4633f71e016b63e59b.html 同时推荐一个很好的博客&#xff0c;这里有连载的 ns2 仿真问题&#xff0c;感谢大牛&#xff5e;&#xff5e;&#xff5e; 博客地址&#xff1a;http://hi.baidu.com/wirele…

NS2问题解决

问题一&#xff1a; When configured, ns found the right version of tclsh in /usr/bin/tclsh8.6 but it doesnt seem to be there anymore, so ns will fall back on running the first tclsh in your path. The wrong version of tclsh may break the test suites. Reconfi…

NS2简单介绍

NS是一种针对网络技术的源代码公开的、免费的软件模拟平台&#xff0c;研究人员使用它可以很容易的进行网络技术的开发&#xff0c;而且发展到今天&#xff0c;它所包含的模块已经非常丰富&#xff0c;几乎涉及到了网络技术的所有方面。所以&#xff0c;NS成了目前学术界广泛使…

NS2网络仿真

NS2安装与配置TCP/UDP比较仿真静态/动态路由仿真 1.安装与配置 1.1更新系统 sudo apt-get update #更新源列表 sudo apt-get upgrade #更新已经安装的包 sudo apt-get dist-upgrade #更新软件&#xff0c;升级系统 1.2安装ns2需要的几个包 sudo apt-get install build-ess…

ns2安装详细过程与网络仿真

ns2安装详细过程与网络仿真 博客分类&#xff1a; Networks TclLinuxUnixGCCVC 简单的说&#xff0c;NS&#xff0d;2是一个网络模拟器&#xff0c;所以经常被用到网络课的教学中。 NS-2是OpenSource的&#xff0c;最早的版本是在linux/unix下运行的&#xff0c;后来有了wi…

NS2教程

柯老师的NS2新网址 Due to some reasons, my NS2 website is sometimes donw and unavailable for many users. Therefore, I provide another backup website. 1. NS2 http://csie.nqu.edu.tw/smallko/ns2/ns2.htm 2. old_NS2 (backup of NS2 Learning Guide) http://csie.n…

ns2安装和若干问题的解决方法

文章目录 1. 安装与配置2. 安装nam3. 配置环境变量4. 检查是否能够成功运行参考资料 在安装ns2的过程中遇到了很多问题&#xff0c;为了记录这些问题和为同样遇到这些问题的朋友提供思路&#xff0c;写下这篇博文。 安装ns2和nam主要分为如下几个步骤&#xff1a; 安装与配置…

Linux下安装ns2

最近为了项目需要用到NS2软件用于网络仿真实验&#xff0c;从Windows到Linux折腾了我将近一周的时间。在Windows装了卸、卸了装十几遍还是不成功&#xff0c;最后放弃了&#xff0c;回到了Linux系统&#xff0c;又折腾了两天终于安装测试成功&#xff01;&#xff08;安装其实蛮…