天天看点

使用Mahout实现协同过滤接口相关介绍单机运行在Spark中运行分布式运行

Mahout算法框架自带的推荐器有下面这些:

GenericUserBasedRecommender:基于用户的推荐器,用户数量少时速度快;

GenericItemBasedRecommender:基于商品推荐器,商品数量少时速度快,尤其当外部提供了商品相似度数据后效率更好;

SlopeOneRecommender:基于slope-one算法的推荐器,在线推荐或更新较快,需要事先大量预处理运算,物品数量少时较好;

SVDRecommender:奇异值分解,推荐效果较好,但之前需要大量预处理运算;

KnnRecommender:基于k近邻算法(KNN),适合于物品数量较小时;

TreeClusteringRecommender:基于聚类的推荐器,在线推荐较快,之前需要大量预处理运算,用户数量较少时效果好;

Mahout最常用的三个推荐器是上述的前三个,本文主要讨论前两种的使用。

基于用户或物品的推荐器主要包括以下几个接口:

<code>DataModel</code> 是用户喜好信息的抽象接口,它的具体实现支持从任意类型的数据源抽取用户喜好信息。Taste 默认提供 JDBCDataModel 和 FileDataModel,分别支持从数据库和文件中读取用户的喜好信息。

<code>UserSimilarity</code> 和 <code>ItemSimilarity</code>。UserSimilarity 用于定义两个用户间的相似度,它是基于协同过滤的推荐引擎的核心部分,可以用来计算用户的“邻居”,这里我们将与当前用户口味相似的用户称为他的邻居。ItemSimilarity 类似的,计算内容之间的相似度。

<code>UserNeighborhood</code> 用于基于用户相似度的推荐方法中,推荐的内容是基于找到与当前用户喜好相似的邻居用户的方式产生的。UserNeighborhood 定义了确定邻居用户的方法,具体实现一般是基于 UserSimilarity 计算得到的。

<code>Recommender</code> 是推荐引擎的抽象接口,Taste 中的核心组件。程序中,为它提供一个 DataModel,它可以计算出对不同用户的推荐内容。实际应用中,主要使用它的实现类 GenericUserBasedRecommender 或者 GenericItemBasedRecommender,分别实现基于用户相似度的推荐引擎或者基于内容的推荐引擎。

<code>RecommenderEvaluator</code>:评分器。

<code>RecommenderIRStatsEvaluator</code>:搜集推荐性能相关的指标,包括准确率、召回率等等。

目前,Mahout为DataModel提供了以下几种实现:

org.apache.mahout.cf.taste.impl.model.GenericDataModel

org.apache.mahout.cf.taste.impl.model.GenericBooleanPrefDataModel

org.apache.mahout.cf.taste.impl.model.PlusAnonymousUserDataModel

org.apache.mahout.cf.taste.impl.model.file.FileDataModel

org.apache.mahout.cf.taste.impl.model.hbase.HBaseDataModel

org.apache.mahout.cf.taste.impl.model.cassandra.CassandraDataModel

org.apache.mahout.cf.taste.impl.model.mongodb.MongoDBDataModel

org.apache.mahout.cf.taste.impl.model.jdbc.SQL92JDBCDataModel

org.apache.mahout.cf.taste.impl.model.jdbc.MySQLJDBCDataModel

org.apache.mahout.cf.taste.impl.model.jdbc.PostgreSQLJDBCDataModel

org.apache.mahout.cf.taste.impl.model.jdbc.GenericJDBCDataModel

org.apache.mahout.cf.taste.impl.model.jdbc.SQL92BooleanPrefJDBCDataModel

org.apache.mahout.cf.taste.impl.model.jdbc.MySQLBooleanPrefJDBCDataModel

org.apache.mahout.cf.taste.impl.model.jdbc.PostgreBooleanPrefSQLJDBCDataModel

org.apache.mahout.cf.taste.impl.model.jdbc.ReloadFromJDBCDataModel

<code>UserSimilarity</code> 和 <code>ItemSimilarity</code> 相似度实现有以下几种:

<code>CityBlockSimilarity</code>:基于Manhattan距离相似度

<code>EuclideanDistanceSimilarity</code>:基于欧几里德距离计算相似度

<code>LogLikelihoodSimilarity</code>:基于对数似然比的相似度

<code>PearsonCorrelationSimilarity</code>:基于皮尔逊相关系数计算相似度

<code>SpearmanCorrelationSimilarity</code>:基于皮尔斯曼相关系数相似度

<code>TanimotoCoefficientSimilarity</code>:基于谷本系数计算相似度

<code>UncenteredCosineSimilarity</code>:计算 Cosine 相似度

UserNeighborhood 主要实现有两种:

NearestNUserNeighborhood:对每个用户取固定数量N个最近邻居

ThresholdUserNeighborhood:对每个用户基于一定的限制,取落在相似度限制以内的所有用户为邻居

Recommender分为以下几种实现:

GenericUserBasedRecommender:基于用户的推荐引擎

GenericBooleanPrefUserBasedRecommender:基于用户的无偏好值推荐引擎

GenericItemBasedRecommender:基于物品的推荐引擎

GenericBooleanPrefItemBasedRecommender:基于物品的无偏好值推荐引擎

RecommenderEvaluator有以下几种实现:

<code>AverageAbsoluteDifferenceRecommenderEvaluator</code>:计算平均差值

<code>RMSRecommenderEvaluator</code>:计算均方根差

RecommenderIRStatsEvaluator的实现类是GenericRecommenderIRStatsEvaluator。

首先,需要在maven中加入对mahout的依赖:

基于用户的推荐,以FileDataModel为例:

注意: FileDataModel要求输入文件中的字段分隔符为逗号或者制表符,如果你想使用其他分隔符,你可以扩展一个FileDataModel的实现,例如,mahout中已经提供了一个解析MoiveLens的数据集(分隔符为<code>::</code>)的实现GroupLensDataModel。

GenericUserBasedRecommender是基于用户的简单推荐器实现类,推荐主要参照传入的DataModel和UserNeighborhood,总体是三个步骤:

(1) 从UserNeighborhood获取当前用户Ui最相似的K个用户集合{U1, U2, …Uk};

(2) 从这K个用户集合排除Ui的偏好商品,剩下的Item集合为{Item0, Item1, …Itemm};

(3) 对Item集合里每个Itemj计算Ui可能偏好程度值pref(Ui, Itemj),并把Item按此数值从高到低排序,前N个item推荐给用户Ui。

对相同用户重复获得推荐结果,我们可以改用CachingRecommender来包装GenericUserBasedRecommender对象,将推荐结果缓存起来:

上面代码可以在main方法中直接运行,然后,我们可以获取推荐模型的评分:

接下来,可以获取推荐结果的查准率和召回率:

如果是基于物品的推荐,代码大体相似,只是没有了UserNeighborhood,然后将上面代码中的User换成Item即可,完整代码如下:

在Spark中运行,需要将Mahout相关的jar添加到Spark的classpath中,修改/etc/spark/conf/spark-env.sh,添加下面两行代码:

然后,以本地模式在spark-shell中运行下面代码交互测试:

Mahout提供了<code>org.apache.mahout.cf.taste.hadoop.item.RecommenderJob</code>类以MapReduce的方式来实现基于物品的协同过滤,查看该类的使用说明:

可见,该类可以接收的命令行参数如下:

<code>--input(path)</code>: 存储用户偏好数据的目录,该目录下可以包含一个或多个存储用户偏好数据的文本文件;

<code>--output(path)</code>: 结算结果的输出目录

<code>--numRecommendations (integer)</code>: 为每个用户推荐的item数量,默认为10

<code>--usersFile (path)</code>: 指定一个包含了一个或多个存储userID的文件路径,仅为该路径下所有文件包含的userID做推荐计算 (该选项可选)

<code>--itemsFile (path)</code>: 指定一个包含了一个或多个存储itemID的文件路径,仅为该路径下所有文件包含的itemID做推荐计算 (该选项可选)

<code>--filterFile (path)</code>: 指定一个路径,该路径下的文件包含了<code>[userID,itemID]</code>值对,userID和itemID用逗号分隔。计算结果将不会为user推荐<code>[userID,itemID]</code>值对中包含的item (该选项可选)

<code>--booleanData (boolean)</code>: 如果输入数据不包含偏好数值,则将该参数设置为true,默认为false

<code>--maxPrefsPerUser (integer)</code>: 在最后计算推荐结果的阶段,针对每一个user使用的偏好数据的最大数量,默认为10

<code>--minPrefsPerUser (integer)</code>: 在相似度计算中,忽略所有偏好数据量少于该值的用户,默认为1

<code>--maxSimilaritiesPerItem (integer)</code>: 针对每个item的相似度最大值,默认为100

<code>--maxPrefsPerUserInItemSimilarity (integer)</code>: 在item相似度计算阶段,针对每个用户考虑的偏好数据最大数量,默认为1000

<code>--similarityClassname (classname)</code>: 向量相似度计算类

<code>outputPathForSimilarityMatrix</code>:SimilarityMatrix输出目录

<code>--randomSeed</code>:随机种子 –<code>sequencefileOutput</code>:序列文件输出路径

<code>--tempDir (path)</code>: 存储临时文件的目录,默认为当前用户的home目录下的temp目录

<code>--startPhase</code>

<code>--endPhase</code>

<code>--threshold (double)</code>: 忽略相似度低于该阀值的item对

一个例子如下,使用SIMILARITY_LOGLIKELIHOOD相似度推荐物品:

默认情况下,mahout使用的reduce数目为1,这样造成大数据处理时效率较低,可以通过参数mahout执行脚本中的<code>MAHOUT_OPTS</code>中的<code>-Dmapred.reduce.tasks</code>参数指定reduce数目。

上面命令运行完成之后,会在当前用户的hdfs主目录生成temp目录,该目录可由<code>--tempDir (path)</code>参数设置:

观察yarn的管理界面,该命令会生成9个任务,任务名称依次是:

PreparePreferenceMatrixJob-ItemIDIndexMapper-Reducer

PreparePreferenceMatrixJob-ToItemPrefsMapper-Reducer

PreparePreferenceMatrixJob-ToItemVectorsMapper-Reducer

RowSimilarityJob-CountObservationsMapper-Reducer

RowSimilarityJob-VectorNormMapper-Reducer

RowSimilarityJob-CooccurrencesMapper-Reducer

RowSimilarityJob-UnsymmetrifyMapper-Reducer

partialMultiply

RecommenderJob-PartialMultiplyMapper-Reducer

从任务名称,大概可以知道每个任务在做什么,如果你的输入参数不一样,生成的任务数可能不一样,这个需要测试一下才能确认。

在hdfs上查看输出的结果,用户和推荐结果用<code>\t</code>分隔,推荐结果中物品之间用逗号分隔,物品后面通过冒号连接评分:

在Scala或者Spark中,可以以Java API或者命令方式运行,最后还可以通过Spark来处理推荐的结果,例如:过滤、去重、补足数据,这部分内容不做介绍。