最近, 我们一直在为Yap.TV实施推荐系统:在安装应用程序并转到“ Just for you”选项卡后,您可以看到它的运行情况。 我们以Apache Mahout为基础进行建议。 Mahout是一个“可扩展的机器学习库”,其中包含使用协作过滤算法的基于用户和项目的推荐者的本地和分布式实现。
现在,我们将专注于本地单机实施。 如果您拥有数千万的首选项值,它应该会很好地工作。 除此之外,您可能应该考虑基于Hadoop的实现,因为数据根本无法放入内存中。
用Mahout编写基本的推荐器非常简单; 由于Mahout的可配置性很强,因此通常有不同的实现方式可供选择。 我只描述我认为是“好的起点”。
基本
首先,您需要一个包含输入数据的文件。 格式非常简单:以逗号分隔的(用户ID,商品ID)对或(用户ID,商品ID,偏好值)三倍。 这表示您已经知道:哪些用户喜欢哪些项目,以及可选多少(例如1-5级)。 id必须为整数,首选项值被视为浮点型。
让我们首先创建一个基于用户的推荐器:这是一个推荐器,当被问到对用户A的推荐时,它首先会查找与“ A”相似的用户,然后尝试查找这些相似的用户已评价过的最佳商品,但A还没有。 为此,我们需要创建4个组件:
- 数据模型 :这将使用文件
- 用户相似度 :给定两个用户的度量,将返回一个数字,表示他们的相似度
- 邻域 :用于查找给定用户的邻域
- 推荐器 :将这些片段组合在一起以产生推荐
对于一元输入数据(用户喜欢项目或我们不知道的数据),一个好的起点是:
val dataModel = new FileDataModel(file)
val userSimilarity = new LogLikelihoodSimilarity(dataModel)
val neighborhood = new NearestNUserNeighborhood(25, userSimilarity, dataModel)
val recommender = new GenericBooleanPrefUserBasedRecommender(dataModel, neighborhood, userSimilarity)
如果我们有偏好值(输入数据中的三倍):
val dataModel = new FileDataModel(file)
val userSimilarity = new PearsonCorrelationSimilarity(dataModel)
val neighborhood = new NearestNUserNeighborhood(25, userSimilarity, dataModel)
val recommender = new GenericUserBasedRecommender(dataModel, neighborhood, userSimilarity)
现在我们准备得到一些建议; 这很简单:
// Gets 10 recommendations
val result = recommender.recommend(userId, 10)
// We get back a list of item-estimated preference value,
// sorted from the highest score
result.foreach(r => println(r.getItemID() + ": " + r.getValue()))
线上
在线方面呢? 以上内容对现有用户非常有用; 在服务中注册的新用户呢? 当然,我们也想为他们提供一些合理的建议。 创建推荐器实例非常昂贵(肯定会比“正常”网络请求花费更长的时间),因此我们不能每次都创建一个新的推荐器。
幸运的是Mahout可以将临时用户添加到数据模型中。 常规设置如下:
- 使用当前数据定期重新创建整个推荐器(例如每天或每小时-取决于需要多长时间)
- 进行推荐时,请检查用户是否存在于系统中
- 如果是,请像往常一样做建议
- 如果不是,请创建一个临时用户,填写首选项,然后进行建议
如果内存有限,第一部分(定期重新创建推荐器)实际上可能会很棘手:创建新推荐器时,您需要在内存中保存两个数据副本(以便仍然能够处理来自服务器的请求老)。 但这实际上与建议没有任何关系,因此在这里我将不做详细介绍。
对于临时用户,我们可以使用PlusAnonymousConcurrentUserDataModel
实例包装数据模型。 此类允许获取临时用户ID。 该ID必须稍后发布,以便可以重复使用(此类ID的数量有限)。 获取ID后,我们必须填写首选项,然后我们可以像往常一样继续进行推荐:
val dataModel = new PlusAnonymousConcurrentUserDataModel(
new FileDataModel(file),
100)
val recommender: org.apache.mahout.cf.taste.recommender.Recommender = ...
// we are assuming a unary model: we only know which items a user likes
def recommendFor(userId: Long, userPreferences: List[Long]) = {
if (userExistsInDataModel(userId)) {
recommendForExistingUser(userId)
} else {
recommendForNewUser(userPreferences)
}
}
def recommendForNewUser(userPreferences: List[Long]) = {
val tempUserId = dataModel.takeAvailableUser()
try {
// filling in a Mahout data structure with the user's preferences
val tempPrefs = new BooleanUserPreferenceArray(userPreferences.size)
tempPrefs.setUserID(0, tempUserId)
userPreferences.zipWithIndex.foreach { case (preference, idx) =>
tempPrefs.setItemID(idx, preference)
}
dataModel.setTempPrefs(tempPrefs, tempUserId)
recommendForExistingUser(tempUserId)
} finally {
dataModel.releaseUser(tempUserId)
}
}
def recommendForExistingUser(userId: Long) = {
recommender.recommend(userId, 10)
}
整合业务逻辑
由于某些业务规则,我们经常想提高所选项目的得分。 在我们的用例中,例如,如果某节目有新剧集,我们希望给它更高的分数。 使用Mahout的IDRescorer
接口可以实现。 调用Recommender.recommend
时,提供了一个rescorer实例。 例如:
val rescorer = new IDRescorer {
def rescore(id: Long, originalScore: Double) = {
if (showIsNew(id)) {
originalScore * 1.2
} else {
originalScore
}
}
def isFiltered(id: Long) = false
}
// Gets 10 recommendations
val result = recommender.recommend(userId, 10, rescorer)
摘要
Mahout是创建推荐器的重要基础。 它是非常可配置的,并提供许多扩展点。 选择正确的配置参数值,设置评分和评估推荐结果还有很多工作要做,但是算法是可靠的,因此无需担心。
还有一本非常好的书,《 Mahout in Action》 ,涵盖了推荐系统和Mahout的其他组件。 它基于版本0.5(当前版本为0.8),但是代码示例大部分都可以工作,并且项目的主要逻辑是相同的。
翻译自: https://www.javacodegeeks.com/2013/10/creating-an-on-line-recommender-system-with-apache-mahout.html