Chinaunix首页 | 论坛 | 博客
  • 博客访问: 23597
  • 博文数量: 13
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 136
  • 用 户 组: 普通用户
  • 注册时间: 2015-01-22 14:23
文章分类

全部博文(13)

文章存档

2015年(13)

我的朋友

分类: Python/Ruby

2015-07-03 15:27:28

    还没看完第1章,我就决定将这本书以博客的形式记载下来,确切地说是以自己读的时候,加上敲代码的时候的思考整理下来。另外附加一句,虽然python我不是很熟悉,但是不会刻意的将python知识记录到这个读书笔记中。好,下面进入正题。

评分机制    

说到推荐系统,自然首先需要一个评分机制(这个是为了后面能够准确描述相似性而必须的),我们使用嵌套字典。
   

点击(此处)折叠或打开

  1. critics = {'Lisa Rose' : {'Lady in the Water' : 2.5, 'Snakes on a Plane' : 3.5, 'Just My Luck' : 3.0, 'Superman Returns' : 3.5, 'You, Me and Dupree' : 2.5, 'The Night Listener' : 3.0},
  2.            'Gene Seymour' : {'Lady in the Water' : 3.0, 'Snakes on a Plane' : 3.5, 'Just My Luck' : 1.5, 'Superman Returns' : 5.0, 'The Night Listener' : 3.0, 'You, Me and Dupree' : 3.5},
  3.            'Michael Phillips' : {'Lady in the Water' : 2.5, 'Snakes on a Plane' : 3.0, 'Superman Returns' : 3.5, 'The Night Listener' : 4.0},
  4.            'Claudia Puig' : {'Snakes on a Plane' : 3.5, 'Just My Luck' : 3.0, 'The Night Listener' : 4.5, 'Superman Returns' : 4.0, 'You, Me and Dupree' : 2.5},
  5.            'Mick LaSalle' : {'Lady in the Water' : 3.0, 'Snakes on a Plane' : 4.0, 'Just My Luck' : 2.0, 'Superman Returns' : 3.0, 'The Night Listener' : 3.0, 'You, Me and Dupree' : 2.0},
  6.            'Jack Matthews' : {'Lady in the Water' : 3.0, 'Snakes on a Plane' : 4.0, 'The Night Listener' : 3.0, 'Superman Returns' : 5.0, 'You, Me and Dupree' : 3.5},
  7.            'Toby' : {'Snakes on a Plane' : 4.5, 'You, Me and Dupree' : 1.0, 'Superman Returns' : 4.0}}
上述字典使用从1到5的评分,以此来体现每位影评者对某一给定影片的喜爱程度。

相似性

现在我们要有一种方法来确定人们在品味方面的相似程度。书中介绍两套计算相似度评价值的体系:欧几里得距离和皮尔逊相关度。
欧几里得距离评价方法非常符合直观思维(初中我们就开始接触这个逻辑了),不多做解释。明显有偏好越相似的人,其距离就越短。不过,这里我们引入一个函数

点击(此处)折叠或打开

  1. 1 / (1 + 欧几里得距离)
之所以不采用倒数,是为了避免除以0的错误。另外这样取值范围就是(0, 1],并且越相似取值越大。计算相似度的代码:

点击(此处)折叠或打开

  1. from math import sqrt
  2. # 返回一个有关person1与person2的基于距离的相似度评价
  3. def sim_distance(prefs, person1, person2):
  4.     #得到shared_items的列表
  5.     si = {}
  6.     for item in prefs[person1]:
  7.         if item in prefs[person2]:
  8.             si[item] = 1
  9.     #如果两者没有共同之处,则返回0
  10.     if len(si) == 0: return 0
  11.     #计算所有差值的平方和
  12.     sum_of_squares = sum([pow(prefs[person1][item] - prefs[person2][item], 2)
  13.                           for item in si])
  14.     return 1 / (1 + sqrt(sum_of_squares))

皮尔逊相关度评价

    首先我们来了解下,什么叫做皮尔逊相关系数,以下来自百度百科:

就是说,求得的皮尔逊相关系数r是用来描述X和Y向量的线性相关度的:当r > 0,则正相关,当r < 0则负相关。r的绝对值越大(越接近1)则线性相关性越强。r = 0表示不线性相关。
另外,参照这篇博客 http://blog.sina.com.cn/s/blog_60f9c00501012nph.html

相关系数(绝对值)
0.8 - 1.0         极度相关
0.6 - 0.8         强相关
0.4 - 0.6         中等程度相关
0.2 - 0.4         弱相关
0.0 - 0.2         极弱相关或无相关
该博文中还给出了计算皮尔逊相关系数的4个等价公式


我不知道如何证明四个公式等价,但是书上使用的是第4个公式,上代码:

点击(此处)折叠或打开

  1. #返回p1和p2的皮尔逊相关系数
  2. def sim_pearson(prefs, p1, p2):
  3.     #得到双方都曾评价过的物品列表
  4.     si = {}
  5.     for item in prefs[p1]:
  6.         if item in prefs[p2]: si[item] = 1

  7.     #得到列表元素的个数
  8.     n = len(si)

  9.     #如果两者没有共同之处,则返回1
  10.     if n == 0: return 1

  11.     #对所有偏好求和
  12.     sum1 = sum([prefs[p1][it] for it in si])
  13.     sum2 = sum([prefs[p2][it] for it in si])

  14.     #求平方和
  15.     sum1Sq = sum([pow(prefs[p1][it], 2) for it in si])
  16.     sum2Sq = sum([pow(prefs[p2][it], 2) for it in si])

  17.     #求乘积之和
  18.     pSum = sum([prefs[p1][it] * prefs[p2][it] for it in si])

  19.     #计算皮尔逊评价值
  20.     num = pSum - (sum1 * sum2 / n)
  21.     den = sqrt((sum1Sq - pow(sum1, 2) / n) * (sum2Sq - pow(sum2, 2) / n))
  22.     if den == 0: return 0

  23.     r = num / den
  24.     return r
个人感觉书上有一处是错误的,就是如果两者没有共同之处,则返回1。应该感觉返回0(认为它们不相关,是不是这样???毕竟这里还没有开始计算皮尔选相关系数)。看到这里,强调下,公式4(其他3个公式也一样)是用来计算两个向量X,Y的皮尔逊相关系数,假设的大前提是它们已经有了相同的坐标维。所以上述方法中一开始,先得到双发都评价过的物品列表si。

皮尔逊相关系数是一个使用比较广的线性相关的评价系数,个人感觉要比欧几里得距离靠谱。当然万能的话是具体情境具体分析。书上讲了这么一段话:
如果某人总是倾向于给出比另一个人更高的分值,而二者的分值之差又始终保持一致,则他们依然可能会存在很好的相关性。这是欧几里得距离所不能正确描述的。

最相关的评论者

    有了两个评论者的相似度计算方法(本质上来看,无论是欧几里得距离还是皮尔逊相关系数,计算的都是两个向量的相关系数),那么我们就可以推荐和自己最相关的其他评论者了。

点击(此处)折叠或打开

  1. #从反映偏好的字典中返回最为匹配者
  2. #返回结果的个数和相似度函数均为可选参数
  3. def topMatches(prefs, person, n = 5, similarity = sim_pearson):
  4.     scores = [(similarity(prefs, person, other), other)
  5.                 for other in prefs if other != person]
  6.     #对列表进行排序,评价值最高者排在最前面
  7.     scores.sort()
  8.     scores.reverse()
  9.     return scores[0:n]
到这里也没什么牛B技术,居然就已经有了推荐。。。

推荐物品

   找到一位趣味相投的影评者并阅读他所撰写的评论固然不错。但很多时候,我们更关心的是影片的推荐。当然,我们可以查找与自己品味最为相近的人,并从他所喜欢的影片中找出一部自己还未看过的影片。有时,这种方法可能会有问题:评论者还未对某些影片做过评论,而这些影片也许就是我们所喜欢的。还有一种可能是,我们会找到一个热衷某部影片的古怪评论者,而根据tomMatches所返回的结果,所有其他的评论者都不看好这部影片。
   为解决上述问题,我们引入加权评价值来为影片打分(注意这里是直接从影片的角度来考虑的,而不是上面的从相似的评论者->影片的思路,那是如何处理的呢?) 下表给出了这一方法的执行过程:

(这里我们在干的是这么一件事情,为Toby推荐他没有看过的电影中哪部最值得期待。我按照critics绘制了人和电影的观看关系

其中1就代表已经看过该电影了)
表中列出了每位评论者的相关度评价值,以及他们对三部影片的评分情况。以S.x打头的列给出了乘以评价值之后的相似度。如此一来,相比于与我们不相近的人,那些与我们相近的人将会给整体评价值拥有更多的贡献。总计一行给出了所有加权平价值的总和。
我们也可以选择利用总计值来计算排名,但是考虑到,一部受更多人评论的影片会对结果产生更大的影响。为了修正这一问题,我们需要除以表中名为Sim.Sum的那一行,它代表了所有对这部电影有过评论的评论者的相似度之和(这个可以从S.xNight和S.xLady看出来)。

  1. #利用所有他人评价值的加权平均,为某人提供建议
  2. def getRecommendations(prefs, person, similarity = sim_pearson):
  3.     totals = {}
  4.     simSums = {}
  5.     for other in prefs:
  6.         #不要和自己做比较
  7.         if other == person: continue
  8.         sim = similarity(prefs, person, other)
  9.         #忽略评价值为零或小于零的情况
  10.         if sim <= 0: continue
  11.         for item in prefs[other]:
  12.             #只对自己还未曾看过的影片进行评价
  13.             if item not in prefs[person] or prefs[person][item] == 0:
  14.                 #相似度*评价值
  15.                 totals.setdefault(item, 0)
  16.                 totals[item] += prefs[other][item] * sim
  17.                 #相似度之和
  18.                 simSums.setdefault(item, 0)
  19.                 simSums[item] += sim
  20.     #建立一个归一化的列表
  21.     rankings = [(total / simSums[item], item) for item, total in totals.items()]
  22.     #返回经过排序的列表
  23.     rankings.sort()
  24.     rankings.reverse()
  25.     return rankings

(代码中忽略相似度<=0的情况就是去除了不线性相关或者负相关的情况)

匹配商品

   现在,我们已经知道了如何为指定人员寻找品味相近者,以及如何向其推荐商品的方法。但是假如我们想了解哪些商品是彼此相近的,那又该如何做呢?下图显示了Amazon的广告

事实上,这和我们此前用来决定人和人之间相似度的方法是一样的--只需将人员与物品对换即可。

  1. def transformPrefs(prefs):
  2.     result = {}
  3.     for person in prefs:
  4.         for item in prefs[person]:
  5.             result.setdefault(item, {})
  6.             #将物品和人员对调
  7.             result[item][person] = prefs[person][item]
  8.     return result

  9. movies = transformPrefs(critics)
  10. print(topMatches(movies, 'Superman Returns'))
(这里就是我前文提到的计算相似度的本质,就是sim_pearson方法计算两个向量的相似度)
不仅如此,我们还可以为影片推荐评论者。

  1. print(getRecommendations(movies, 'Just My Luck'))
(这个理解起来有点似懂非懂,具体理解为,先得到所有文章的相似度,然后计算每个评论者每篇文章的贡献值,然后求出每个评论者的贡献值总和并进行归一化。我不确定互换对于任何都有意义)


阅读(738) | 评论(0) | 转发(0) |
0

上一篇:input函数

下一篇:测试时的对象Mock

给主人留下些什么吧!~~