昨天转载过一篇文章,描述的是协同过滤推荐算法。今天利用空余时间,我尝试了一下,当然,第一次实现没有考虑效率的问题,好多流程也是能省则省。写这篇文章,一方面理清自己的思路,另一方面为之后提升算法的实现效率,做一点文字记录。
该算法的核心思想可以概括为:若a,b喜欢同一系列的物品(暂时称b是a的邻居吧),则a很可能喜欢b喜欢的其他物品。算法的实现流程可以简单概括为:1.确定a有哪些邻居 2.通过邻居来预测a可能会喜欢哪种物品 3.将a可能喜欢的物品推荐给a。
算法核心的公式如下:
1.余弦相似度(求邻居):
2.预测公式(预测a可能会喜欢哪种物品):
仅从这两个公式我们就可以看出,仅仅是按照这两个公式进行计算,就需要进行大量的循环与判断,而且还涉及到排序的问题,就涉及到排序算法的选择与使用,这里我选快排,从网上copy了一段快排,直接用。总之实现起来很麻烦,在大数据情况下,更何谈效率。
首先建表:
我这里只对最后一行的Leo进行推荐,看看f,g,h哪个可以推荐给他。
用php+mysql,流程图如下:
连接数据库并将其存储为二维数组的代码如下:
问题1:这一步完全可以看做是整表查询,这种查询是大忌,对于这种小小的演示系统还可以,但是对大数据的系统,没有效率,至于如何改进,还得多学习才是。
求Leo与其他人的Cos值代码如下:
这一步得到的结果是酱紫:
将求好的Cos值排序,采用快排代码如下(百度copy而来):
这里的$neighbour数组仅仅存储了从大到小排序好的Cos值,并没有与人联系起来。这个问题还要解决。
选出Cos值最高的3个人,作为Leo的邻居:
这一步得到的结果是酱紫:
这是一个二维数组,数组第一层的下标为0,1,2,代表3个人。第二层下标0代表邻居在数据表中的顺序,比如Jhon是表中的第0个人;下标1代表Leo和邻居的Cos值;下标2,3,4分别代表邻居对f,g,h的评分。
开始进行预测,计算Predict代码如下:
我是分别计算Leo对f,g,h的预测值。在此有一个问题,就是如果有的邻居对f,g,h的评分为空,那么该如何处理。比如Jhon和Mary对h的评分就为空。本能的想到用if判断一下,如果为空则跳过这组计算,不过这样处理是否合理,有待考虑。以下代码并没有写出这个if判断。
$p_arr是对Leo的推荐数组,其内容类似如下;
Array ( [0] => Array ( [0] => 6 [1] => 4.2314002228795 ) [1] => Array ( [0] => 7 [1] => 2.6511380196197 ) [2] => Array ( [0] => 8 [1] => 0.45287424581774 ) )
f是第6列,Predict值是4.23,g是第七列,Predict值是2.65........
求完了f,g,h的Predict值后有两种处理方式:一种是将Predict值大于3的物品推荐给Leo,另一种是将Predict值从大到小排序,将Predict值大的前2个物品推荐给Leo。这段代码没有写。
从上面的示例中可以看出,推荐算法的实现非常麻烦,需要循环,判断,合并数组等等。如果处理不当,反而会成为系统的累赘。在实际处理中还有以下问题:
1.以上示例我们只对Leo进行推荐,而且我们已经知道Leo没有评价过f,g,h物品。如果放到实际的系统里,对于每一个需要进行推荐的用户,都要查询出他没有评价过哪些物品,这又是一部分开销。
2.不应当进行整表查询,在实际系统中可以设定一些标准值。比如:我们求Leo与表中的其他人的Cos值,如果该值大于0.80,则表示可以为邻居。这样,当我找到10个邻居之后,就停止求Cos值,避免整表查询。对于推荐物品也可以适当采用此方法,比如,我只推荐10个物品,推荐完后就停止求Predict值。
3.随着系统的使用,物品也会发生变化,今天是fgh,明天没准就是xyz了,当物品变化时,需要动态的改变数据表。
4.可以适当引进基于内容的推荐,来完善推荐算法。
5.推荐的精确性问题,这个设置不同的标准值,会影响精确性。
总结:我觉得本质的问题是算法的效率就不高,继续学习,看看有没有更好的协同过滤推荐算法。