Lenskit备忘录 - Item-Based协同过滤推荐算法笔记

最近参与了一个项目,负责推荐算法的调查和提案。考察了几个流行的工具,最终选择了Lenskit。官网上的文档资源已经比较丰富了,但是为了满足项目需求很多内容需要解读源代码才能理解,所以觉得有必要把学习成果贡献出来,同时也为自己留下一份笔记,以备将来有需要时可以进行查阅。如果您通过阅读本文,得到了启发与收获,绝对是我的荣幸。

Lenskit提供了四种协同过滤算法的实现,Item-Based,User-Based,​Slope-One,和Matrix factorization。本文只涉及第一种算法。

官网:http://lenskit.org/

版本:2.2.1

开源协议​:LGPL


Item-Based推荐算法​

推荐的基本思路是根据某个用户(User)对已使用过物品(Item)的评分进行一系列计算,预测未使用过物品被该用户使用后获得的得分,然后按照预测得分进行降序输出。预测得分最高的物品就是最该向该名用户推荐的物品。具体的计算流程分为两步

1. 计算物品(Item)之间的相似度​

以物品为单位,计算该物品与所有其他物品的相似程度,保存在一个2维矩阵中。​lenskit默认使用余弦相似度公式进行计算。公式如下:

Lenskit备忘录 - Item-Based协同过滤推荐算法笔记公式1

以lenskit-hello中的电影推荐为例,是将所有n名用户对某个物品的打分看作一个向量空间模型,那么Item1与Item2的相似度就是这两个向量在该n维向量空间中的余弦夹角。假设5名用户对两个物品有如下打分:

Lenskit备忘录 - Item-Based协同过滤推荐算法笔记表1

​未打分的按0处理,则计算结果为:

Lenskit备忘录 - Item-Based协同过滤推荐算法笔记公式2

假设五个用户对五个Item有如下打分:

Lenskit备忘录 - Item-Based协同过滤推荐算法笔记表2

​那么五个Item之间的相似度为:

Lenskit备忘录 - Item-Based协同过滤推荐算法笔记表3

Lenskit将相似度矩阵保存在 LenskitRecommenderEngine 对象中为预测分数提供依据。当物品数量较多时,计算的时间会比较长,所以通常是将其计算好保存在内存或文件中。Lenskit也提供了相关API,详见官网文档

http://lenskit.org/documentation/basics/data-access/​

2. 预测推荐分数

​预测过程也分为两步,首先确定预测分数所需要的样本,使用最邻近算法((kNN, K-NearestNeighbor) 来确定被预测物品的近邻 (Neighbor),近邻的判断标准可以简单地定义为 与被测物品相似并且被该名用户打了分数的其他物品。例如,预测User1对Item2的打分时,参考表2和表3的数据,得知近邻为Item1和Item4

Lenskit备忘录 - Item-Based协同过滤推荐算法笔记表4

注:lenskit提供了一个参数​NeighborhoodSize,默认最小为2最大为20,即至少找到2个以上的近邻才开始进行预测,我在一开始调查时由于给的测试数据太过稀松,导致预测不出来分数,后来看了程序才知道这个逻辑。

在确定了近邻后,利用所有近邻的相似度和已知得分,按照以下公式就计算出目标物品的预测分数。

Lenskit备忘录 - Item-Based协同过滤推荐算法笔记公式3

​User1对Item2的预测分数为:2.31

基于标签的推荐

在本次项目中,并没有用户给物品打分的业务,系统给所有的物品和用户都设定了标签(Tag)并记录了用户的行动履历,履历包括用户对某物品进行了收藏,或者下载了该物品的附属文件等等。客户希望基于用户身上的标签 和 物品标签的匹配程度进行推荐。针对这个需求,只要计算出用户与物品的相似度即可。前文提到的lenskit-hello工程是基于用户对电影打分进行的推荐的,将本次项目中定义的所有的标签视为lenskit-hello中的用户,如果某个物品拥有这个标签,则视为该标签对该物品打了3分,用户同样视为物品。计算出相似度,然后按照相似度由高到低排序来满足需求,不需要预测推荐分数的环节。下列代码的第7行就是取得相似度矩阵的代码,第8行是取得某用户与其他物品的相似度,用户ID为-3。

Lenskit备忘录 - Item-Based协同过滤推荐算法笔记相似度矩阵取得

基于行动履历的推荐

lenskit-hello工程根据用户对电影的打分来计算相似度和预测分数。基于用户履历的推荐可以考虑将用户对某物品进行的操作转化为打分。例如如果对物品进行了下载那么加两分,如果进行了收藏加三分。Lenskit源代码中有一个类叫LikeCountUserHistorySummarizer

基本上实现了这种思路,该类的计算方法为用户对某物品每Like一次则加一分。lenskit-hello工程默认使用RatingVectorUserHistorySummarizer汇总用户的打分,我们参照LikeCount的程序,新增了一个MyEventUserHistorySummarizer类将默认的类替换掉来满足需求。具体的配置方法可以参考官网文档,本文不再赘述。

以上是本人对Lenskit开源工具的一点体会,才疏学浅,欢迎大家批评指正。