本文介绍推荐程序的简单创建与常用接口/类的简单介绍,深入学习请参考文中的扩展链接与mahout官网。
1.输入文件item.csv
数据格式:用户ID、物品ID、偏好值
1,101,5.0 1,102,3.0 1,103,2.5 2,101,2.0 2,102,2.5 2,103,5.0 2,104,2.0 3,101,2.5 3,104,4.0 3,105,4.5 3,107,5.0 4,101,5.0 4,103,3.0 4,104,4.5 4,106,4.0 5,101,4.0 5,102,3.0 5,103,2.0 5,104,4.0 5,105,3.5 5,106,4.02.一个简单的基于用户的推荐程序
package xyz.baal.mahout; import java.io.File; import java.io.IOException; import java.util.List; import org.apache.mahout.cf.taste.common.TasteException; import org.apache.mahout.cf.taste.impl.model.file.FileDataModel; import org.apache.mahout.cf.taste.impl.neighborhood.ThresholdUserNeighborhood; import org.apache.mahout.cf.taste.impl.recommender.GenericUserBasedRecommender; import org.apache.mahout.cf.taste.impl.similarity.PearsonCorrelationSimilarity; import org.apache.mahout.cf.taste.model.DataModel; import org.apache.mahout.cf.taste.neighborhood.UserNeighborhood; import org.apache.mahout.cf.taste.recommender.RecommendedItem; import org.apache.mahout.cf.taste.recommender.UserBasedRecommender; import org.apache.mahout.cf.taste.similarity.UserSimilarity; /** * 基于用户的推荐程序 * @progect Mahout_0.9 * @file RecUserBasedExample.java * @author baalhuo * @date 2016年10月18日 */ public class RecUserBasedExample { public static void main(String[] args) throws IOException, TasteException { // 加载数据 DataModel model = new FileDataModel(new File("item.csv")); // 相似性度量 UserSimilarity similarity = new PearsonCorrelationSimilarity(model); // 用户邻域 UserNeighborhood neighborhood = new ThresholdUserNeighborhood(0.1, similarity, model); // 生成推荐引擎 UserBasedRecommender recommender = new GenericUserBasedRecommender(model, neighborhood, similarity); // 为用户1 推荐2个物品 List<RecommendedItem> recommendations = recommender.recommend(1, 2); for (RecommendedItem recommendation : recommendations) { System.out.println(recommendation); } } }体验过推荐之后,我们来看看推荐程序的整个流程。
3.基于用户推荐程序中组建关系及组件之间刷新数据的顺序
一个简单的基于用户的推荐系统,箭头所示为组件之间刷新数据结构的顺序
通过以上程序与图片可以了解到一个简单推荐程序的创建过程。
此外,可看出一个基于用户的推荐程序主要包括以下几个接口:
DataModel:数据模型的抽象接口,它的具体实现支持从任意类型的数据源抽取用户喜好信息。Taste 默认提供 JDBCDataModel 和 FileDataModel,分别支持从数据库和文件中读取用户的喜好信息。
UserSimilarity(基于物品的是ItemSimilarity):UserSimilarity用于定义两个用户间的相似度,它是基于协同过滤的推荐引擎的核心部分,可以用来计算用户的“邻居”,这里我们将与当前用户口味相似的用户称为他的邻居。ItemSimilarity 类似的,计算内容之间的相似度。
UserNeighborhood:用于基于用户相似度的推荐方法中,推荐的内容是基于找到与当前用户喜好相似的邻居用户的方式产生的。UserNeighborhood 定义了确定邻居用户的方法,具体实现一般是基于 UserSimilarity 计算得到的。
Recommender:是推荐引擎的抽象接口,Taste 中的核心组件。程序中,为它提供一个 DataModel,它可以计算出对不同用户的推荐内容。实际应用中,主要使用它的实现类 GenericUserBasedRecommender 或者 GenericItemBasedRecommender,分别实现基于用户相似度的推荐引擎或者基于内容的推荐引擎。
在介绍这些接口之前再看一下基于物品的推荐,基于物品的推荐比基于用户的推荐少去一个Neighborhood环节。
4.一个简单的基于物品的推荐程序
package xyz.baal.mahout; import java.io.File; import java.io.IOException; import java.util.List; import org.apache.mahout.cf.taste.common.TasteException; import org.apache.mahout.cf.taste.impl.common.LongPrimitiveIterator; import org.apache.mahout.cf.taste.impl.model.file.FileDataModel; import org.apache.mahout.cf.taste.impl.recommender.GenericItemBasedRecommender; import org.apache.mahout.cf.taste.impl.similarity.PearsonCorrelationSimilarity; import org.apache.mahout.cf.taste.model.DataModel; import org.apache.mahout.cf.taste.recommender.RecommendedItem; import org.apache.mahout.cf.taste.recommender.Recommender; import org.apache.mahout.cf.taste.similarity.ItemSimilarity; /** * 基于物品的推荐程序 * @progect Mahout_0.9 * @file RecItemBasedExample.java * @author baalhuo * @date 2016年10月18日 */ public class RecItemBasedExample { public static void main(String[] args) throws IOException, TasteException { DataModel model = new FileDataModel(new File("item.csv")); ItemSimilarity similarity = new PearsonCorrelationSimilarity(model); Recommender r = new GenericItemBasedRecommender(model, similarity); LongPrimitiveIterator iter = model.getUserIDs(); //为每个用户推荐一件物品 while (iter.hasNext()) { long usid = iter.nextLong(); List<RecommendedItem> list = r.recommend(usid, 1); System.out.print("usid[" + usid + "]"); for (RecommendedItem recommendedItem : list) { System.out.print(recommendedItem); } System.out.println(); } } }
扩展:http://www.jianshu.com/p/e564e1324a5e
5.推荐程序的数据模型(DataModel)
(1).内存级数据--GenericDataModel
使用不多,具体使用见Mahout in Action
(2).基于文件的数据--FileDataModel
以上两个例子中已使用,更多API使用参考官方文档。
(3).基于数据库的数据--JDBCDataModel
package xyz.baal.recommender; import java.util.List; import org.apache.mahout.cf.taste.common.TasteException; import org.apache.mahout.cf.taste.impl.common.LongPrimitiveIterator; import org.apache.mahout.cf.taste.impl.model.jdbc.MySQLJDBCDataModel; import org.apache.mahout.cf.taste.impl.neighborhood.NearestNUserNeighborhood; import org.apache.mahout.cf.taste.impl.recommender.GenericUserBasedRecommender; import org.apache.mahout.cf.taste.impl.similarity.EuclideanDistanceSimilarity; import org.apache.mahout.cf.taste.recommender.RecommendedItem; import org.apache.mahout.cf.taste.recommender.Recommender; import org.apache.mahout.cf.taste.similarity.UserSimilarity; import com.mysql.jdbc.jdbc2.optional.MysqlDataSource; /** * 基于用户推荐 * @progect Mahout_0.12.2 * @file UserBasedRecommender.java * @author baalhuo * @date 2016年10月6日 */ public class UserBasedRecommender { public static void main(String[] args) { // 基于数据库的数据 MysqlDataSource dataSource = new MysqlDataSource(); dataSource.setServerName("localhost"); dataSource.setUser("root"); dataSource.setPassword("root"); dataSource.setDatabaseName("mahout"); MySQLJDBCDataModel dataModel = new MySQLJDBCDataModel(dataSource, "preftable", "user", "item", "pref", null); try { UserSimilarity userSimilarity = new EuclideanDistanceSimilarity(dataModel); NearestNUserNeighborhood neighbor = new NearestNUserNeighborhood(2, userSimilarity, dataModel); Recommender r = new GenericUserBasedRecommender(dataModel, neighbor, userSimilarity); LongPrimitiveIterator iter = dataModel.getUserIDs(); while(iter.hasNext()){ long usid = iter.nextLong(); List<RecommendedItem> list = r.recommend(usid, 3);//为用户usid推荐3个物品 System.out.print("usid["+usid+"]"); for (RecommendedItem recommendedItem : list) { System.out.print(recommendedItem); } System.out.println(); } } catch (TasteException e) { e.printStackTrace(); } } }
对于加载HDSF文件有人已经实现,见:
https://issues.apache.org/jira/browse/MAHOUT-1579
https://github.com/apache/mahout/pull/19/files
相似性度量常见有以下几个:
(1).PearsonCorrelationSimilarity:基于皮尔逊相关系数计算相似度
(2).EuclideanDistanceSimilarity:基于欧几里德距离计算相似度
(3).SpearmanCorrelationSimilarity:基于皮尔斯曼相关系数相似度
(4).TanimotoCoefficientSimilarity:基于谷本系数计算相似度
(5).LogLikelihoodSimilarity:基于对数似然比的相似度
(6).CityBlockSimilarity:基于Manhattan距离相似度
(7).UncenteredCosineSimilarity:计算 Cosine 相似度
7.用户邻域(UserNeighborhood)
(1).NearestNUserNeighborhood:对每个用户取固定数量N个最近邻居。
(2).ThresholdUserNeighborhood:对每个用户基于一定的限制,取落在相似度限制以内的所有用户为邻居。
8.推荐算法(Recommender)
GenericUserBasedRecommender: 基于用户的推荐,用户数量相对较少时速度较快。
GenericItemBasedRecommender:基于物品的推荐,物品数量较少时速度较快,外部提供了物品相似度数据后会更加有效率。
SlopeOneRecommender:基于slope-one算法的推荐器,在线推荐或更新较快,需要事先大量预处理运算,物品数量少时较好。
SVDRecommender:效果不错,和slope-one一样,事先需要大量的预处理运算。
KnnItemBasedRecommender:基于最近邻算法的推荐器,物品数量较小时表现良好。
TreeClusteringRecommender:基于聚类的推荐器,在线推荐较快,同时也需要事先大量预处理运算,用户数量相对较少时表现良好。
关于推荐算法的文章:
Mahout的taste推荐系统里的几种Recommender分析
Mahout推荐算法API详解
9.评测推荐结果
RecommenderEvaluator :评分器。
RecommenderIRStatsEvaluator :搜集推荐性能相关的指标,包括准确率、召回率等等。
RecommenderEvaluator 有以下几种实现:
AverageAbsoluteDifferenceRecommenderEvaluator :计算平均差值
RMSRecommenderEvaluator :计算均方根差
RecommenderIRStatsEvaluator 的实现类是GenericRecommenderIRStatsEvaluator
评测一个基于用户的推荐
package recommender; import java.io.File; import java.io.IOException; import org.apache.mahout.cf.taste.common.TasteException; import org.apache.mahout.cf.taste.eval.RecommenderBuilder; import org.apache.mahout.cf.taste.eval.RecommenderEvaluator; import org.apache.mahout.cf.taste.impl.eval.AverageAbsoluteDifferenceRecommenderEvaluator; import org.apache.mahout.cf.taste.impl.eval.RMSRecommenderEvaluator; import org.apache.mahout.cf.taste.impl.model.file.FileDataModel; import org.apache.mahout.cf.taste.impl.neighborhood.NearestNUserNeighborhood; import org.apache.mahout.cf.taste.impl.recommender.GenericUserBasedRecommender; import org.apache.mahout.cf.taste.impl.similarity.EuclideanDistanceSimilarity; import org.apache.mahout.cf.taste.model.DataModel; import org.apache.mahout.cf.taste.neighborhood.UserNeighborhood; import org.apache.mahout.cf.taste.recommender.Recommender; import org.apache.mahout.cf.taste.similarity.UserSimilarity; import org.apache.mahout.common.RandomUtils; /** * 评测基于用户的推荐 * * @progect Mahout_0.12.2 * @file EvaluateUserBasedRecommender.java * @author baalhuo * @date 2016年10月24日 */ public class EvaluateUserBasedRecommender { public static void main(String[] args) throws IOException, TasteException { // 强制每次选择相同的随机值 生成可重复的结果 RandomUtils.useTestSeed(); DataModel model = new FileDataModel(new File("item.csv")); // 使用平均值计算评分 // RecommenderEvaluator evaluator = new AverageAbsoluteDifferenceRecommenderEvaluator(); // 使用平方根计算评分 RecommenderEvaluator evaluator = new RMSRecommenderEvaluator(); RecommenderBuilder recommenderBuilder = new RecommenderBuilder() { @Override public Recommender buildRecommender(DataModel dataModel) throws TasteException { UserSimilarity similarity = new EuclideanDistanceSimilarity(dataModel); UserNeighborhood neighborhood = new NearestNUserNeighborhood(2, similarity, dataModel); return new GenericUserBasedRecommender(dataModel, neighborhood, similarity); } }; // 训练95%的数据 测试5% score代表估计值与实际值的偏差大小 double score = evaluator.evaluate(recommenderBuilder, null, model, 0.95, 1.0); System.out.println(score); } }
Github:MahoutBeginner
Mahout 0.10.x:API Doc
Mahout in Action:Download