木偶一之推荐系统

打算从这篇开始,一边学习一边写些数据挖掘的东西,主要是督促自己学习和总结。因为都是很基础的入门知识,故称木偶系列,取玩具之意。

我最开始的网购是从china-pub买了一本《Unix/Linux编程实践教程》,书好,便宜,并且可以货到付款,很是吸引我这种懒穷学生,于是一发不可收拾买了很多书,后来转战dangdang,再后来就是amazon,现在基本都在jd买了,除了书,还会在yihaodian买一些日用品。后来发现这几家都会有推荐,dangdang和china-pub的推荐没什么印象,jd的推荐离我的兴趣点差的挺远,印象深刻的就是amazon,有次推送的邮件真是推到我心坎坎了。这次我也来做一个木偶推荐系统玩玩。

好吧,今晚闲来无聊,想找本书来消遣。现在书籍甚是丰富,买哪本书呢

1)我会打电话问问朋友,请求推荐;

2)还会登陆网站看畅销top100,选一本自己感兴趣的;

慢慢就会发现问题,第一:朋友推荐和top100的书籍比较稳定,新意不多,看完了想看新的就没了,第二:这里面很合自己脾气的书不多,自己很厌倦太巨头或太学府派的书,还有就是现在的top100很多孕育和养生的东东,目前我就先忍忍吧。

好了,扯到推荐,怎么推荐呢?我首先想到的是,我喜欢《Unix/Linux编程实践教程》,和这本内容写作风格近似的也应该是我喜欢的,我喜欢packt出版社的一本书,可能这个出版社出版的其他书我也喜欢(事实也是我非常喜欢这个出版社的书),我喜欢《Think in C++》,我可能还会喜欢bruce eckel出的其他书(事实也是这样)。还有一种情况,我是码农,我有个码农朋友,我两兴趣爱好相投,他喜欢的书可能也是我喜欢的书,我喜欢的书也可能是他喜欢的书(事实也是这样)。好,总结一下,第一种基于物品的推荐,我买了A,很可能会喜欢和A相似的B,第二种基于用户推荐,我和C志趣相投,他喜欢D,我可能也喜欢D。


我们先来说说评价,最先想到的评价有两种,买与不买(1和0);买了打几颗星(通常满星是五颗,0,1,2,3,4,5),还有容许打半颗星的,这样就有(0, 0.5,1,1.5,2,2.5 。。。5)。下面我们用0--5之间的小数表示评价,值越打表示评价越高,比如,我喜欢A,我给他5星,一般喜欢B,我给他3.8星,我很讨厌C,我给他0星;于是有了下面一个变量ctitics:

 

critics = {

	'user1': {'goods1': 2.5, 'goods2': 3.5, 'goods3': 3.0, 'goods4': 3.5, 'goods5': 2.5, 'goods6': 3.0},

	'user2': {'goods1': 3.0, 'goods2': 3.5, 'goods3': 1.5, 'goods4': 5.0, 'goods6': 3.0, 'goods5': 3.5}, 

	'user3': {'goods1': 2.5, 'goods2': 3.0, 'goods4': 3.5, 'goods6': 4.0},

	'user4': {'goods2': 3.5, 'goods3': 3.0, 'goods6': 4.5, 'goods4': 4.0, 'goods5': 2.5},

	'user5': {'goods1': 3.0, 'goods2': 4.0, 'goods3': 2.0, 'goods4': 3.0, 'goods6': 3.0, 'goods5': 2.0}, 

	'user6': {'goods1': 3.0, 'goods2': 4.0, 'goods6': 3.0, 'goods4': 5.0, 'goods5': 3.5},

	'user7': {'goods2': 4.5, 'goods5': 1.0, 'goods4': 4.0}

}

 

下面来定义“相似”,“相似”就是在某些方面很接近,怎么考量相似呢,对上面的变量ctitics,怎么判断user1和那个其他user*相似,然后给出推荐呢,首先想到的是欧几里得距离评价,这就是最简单的求两点距离的公式,

下面来计算任意两个用户之间的“相似度”:

 

from math import sqrt



def sim_distance(prefs,person1,person2):

  si={}

  for item in prefs[person1]: 

    if item in prefs[person2]: si[item]=1



  if len(si)==0: return 0



  sum_of_squares=sum([pow(prefs[person1][item]-prefs[person2][item],2) 

                      for item in prefs[person1] if item in prefs[person2]])



  return 1/(1+sqrt(sum_of_squares))


注意最后一行,按照基本的欧几里得距离,两个越相近的人距离值越小,通常我们的做法是近似度越大会给出越大的值,所以这个修正总会返回0-1之间的值,返回1表示两人居右完全相同的爱好。

 


好吧,我们中总会有一些人很挑剔,总有一些人不那么挑剔,这些挑剔的人总趋向于给出整体偏低的评价(3,2,1),有些不那么挑剔的人总趋向与给出整体偏高的评价(5,4,3),但是这两种人的整体偏好相似,都会给goods1一个在他们认为较高的星星数(3和5),都会给goods2一个在他们认为较差的星星数(1和3)。这种情况下,用欧几里得距离计算的结果就不那么具有竞争力了,如何修正这种偏差呢?我们来看看皮尔逊相关度评价



下面来计算用户之间的皮尔逊相关度:

 

def sim_pearson(prefs,p1,p2):

  si={}

  for item in prefs[p1]: 

    if item in prefs[p2]: si[item]=1



  if len(si)==0: return 0



  n=len(si)

  

  sum1=sum([prefs[p1][it] for it in si])

  sum2=sum([prefs[p2][it] for it in si])

  

  sum1Sq=sum([pow(prefs[p1][it],2) for it in si])

  sum2Sq=sum([pow(prefs[p2][it],2) for it in si]) 

  

  pSum=sum([prefs[p1][it]*prefs[p2][it] for it in si])

  

  num=pSum-(sum1*sum2/n)

  den=sqrt((sum1Sq-pow(sum1,2)/n)*(sum2Sq-pow(sum2,2)/n))

  if den==0: return 0



  r=num/den

  return r


还有很多计算相似度的方式,等用到时再学习吧。

 


 下面我们就可以判断任意用户的相似度了:

 

def topMatches(prefs,person,n=5,similarity=sim_pearson):

  scores=[(similarity(prefs,person,other),other) 

                  for other in prefs if other!=person]

  scores.sort()

  scores.reverse()

  return scores[0:n]

注意topMatches的最后一个参数是一个函数名,是我们上面说的计算相似度的任意一种方法,只要他们有相同的函数签名即可,这样我们可以随时换用我们想用的相似度计算方式。
来看看在我机器上计算出来的效果吧:

 



好了,终于找到了臭味相投的同志,给出一个推荐呢?

(1)我们可以从志投道和的user中,找一个他评价很高而我们没有看过的一个goods,

(2)我们在所有志同道合的user中,用相似度和评价的一个加权评价值来给googs打分,从而形成一个排名进而推荐。

很显然,我们采用第二种,为此,我们需要取得其他评论者相似度后,再乘以评论者为每个goods的评价值,就会得到我们想要的排名。

这中间会有一个问题,就是如果一个goods的评论user很多,那么最终排名会比较靠前,而较少评论的goods最最终的影响会比较小,通常这样没什么问题,我们在这里稍微做一些修正,就是用对这个goods评价的所有其他users的相似度之和除以最终的排名值,这样会公平一些,哈哈哈,来看代码:

 

def getRecommendations(prefs,person,similarity=sim_pearson):

  totals={}

  simSums={}

  for other in prefs:

    if other==person: continue

    sim=similarity(prefs,person,other)



    if sim<=0: continue

    for item in prefs[other]:

      

      if item not in prefs[person] or prefs[person][item]==0:

        totals.setdefault(item,0)

        totals[item]+=prefs[other][item]*sim

        simSums.setdefault(item,0)

        simSums[item]+=sim



  rankings=[(total/simSums[item],item) for item,total in totals.items()]



  rankings.sort()

  rankings.reverse()

  return rankings


下面,我们可以进行推荐啦,来看看我计算的结果。user1,user2,user5评价全了所有goods,所以给不出新推荐,这帮贱人。

 



上面我们不仅计算出了相似度,还给出了推荐。值得一提的是,计算相似度不仅可以计算user之间的相似度,还可以计算goods之间的相似度,根据user已买的物品推荐相似度较高的物品在jd和amazon也很常见。


 

 

你可能感兴趣的:(推荐系统)