打算从这篇开始,一边学习一边写些数据挖掘的东西,主要是督促自己学习和总结。
我最开始的网购是从 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))
好吧,我们中总会有一些人很挑剔,总有一些人不那么挑剔,这些挑剔的人总趋向于给出整体偏低的评价(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
下面我们就可以判断任意用户的相似度了:
注意 topMatches 的最后一个参数是一个函数名,是我们上面说的计算相似度的任意一种方法,只要他们有相同的函数签名即可,这样我们可以随时换用我们想用的相似度计算方式。
- 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]
- >>> 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
- >>> 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 也很常见。
来源: http://lib.csdn.net/article/machinelearning/48969