集体智慧编程学习之推荐系统

欢迎关注我的个人博客blog.timene.com

打算从这篇开始,一边学习一边写些数据挖掘的东西,主要是督促自己学习和总结。

我最开始的网购是从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的最后一个参数是一个函数名,是我们上面说的计算相似度的任意一种方法,只要他们有相同的函数签名即可,这样我们可以随时换用我们想用的相似度计算方式。
来看看在我机器上计算出来的效果吧:

>>> import test
>>> test.topMatches(test.critics, 'user1')
[(0.9912407071619299, 'user7'), (0.7470178808339965, 'user6'), (0.5940885257860044, 'user5'), (0.5669467095138396, 'user4'), (0.40451991747794525, 'user3')]
>>> test.topMatches(test.critics, 'user2')
[(0.963795681875635, 'user6'), (0.41176470588235276, 'user5'), (0.39605901719066977, 'user1'), (0.38124642583151164, 'user7'), (0.31497039417435607, 'user4')]
>>> test.topMatches(test.critics, 'user3')
[(1.0, 'user4'), (0.40451991747794525, 'user1'), (0.20459830184114206, 'user2'), (0.13483997249264842, 'user6'), (-0.2581988897471611, 'user5')]
>>> test.topMatches(test.critics, 'user4')
[(1.0, 'user3'), (0.8934051474415647, 'user7'), (0.5669467095138411, 'user5'), (0.5669467095138396, 'user1'), (0.31497039417435607, 'user2')]
>>> test.topMatches(test.critics, 'user5')
[(0.9244734516419049, 'user7'), (0.5940885257860044, 'user1'), (0.5669467095138411, 'user4'), (0.41176470588235276, 'user2'), (0.21128856368212925, 'user6')]
>>> test.topMatches(test.critics, 'user6')
[(0.963795681875635, 'user2'), (0.7470178808339965, 'user1'), (0.66284898035987, 'user7'), (0.21128856368212925, 'user5'), (0.13483997249264842, 'user3')]
>>> test.topMatches(test.critics, 'user7')
[(0.9912407071619299, 'user1'), (0.9244734516419049, 'user5'), (0.8934051474415647, 'user4'), (0.66284898035987, 'user6'), (0.38124642583151164, 'user2')]


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

(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,所以给不出新推荐。

>>> import test
>>> test.getRecommendations(test.critics, 'user1')
[]
>>> test.getRecommendations(test.critics, 'user2')
[]
>>> test.getRecommendations(test.critics, 'user3')
[(2.8092760065251268, 'goods3'), (2.694636703980363, 'goods5')]
>>> test.getRecommendations(test.critics, 'user4')
[(2.683756272799255, 'goods1')]
>>> test.getRecommendations(test.critics, 'user5')
[]
>>> test.getRecommendations(test.critics, 'user6')
[(2.1505590044630245, 'goods3')]
>>> test.getRecommendations(test.critics, 'user7')
[(3.3477895267131013, 'goods6'), (2.832549918264162, 'goods1'), (2.5309807037655645, 'goods3')]



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


你可能感兴趣的:(python,数据挖掘,机器学习,推荐系统)