中英文术语对照表
英文 | 中文 |
---|---|
clustering | j聚类 |
computationally intensive | 计算量很大的 |
cross-product | 叉乘 |
dendrogram | 树状图 |
groups | 群组 |
kernl methods,kernel tricks | 核方法,核技法 |
k-nearest neighbors | k-最近邻 |
multidimensional scalling | 多维缩放 |
pattern | 模式 |
solution | (题)解 |
collective intelligence | 集体智慧 |
crawl | (网页)检索 |
data-intensive | 数据量很大的 |
dot-product | 点积 |
inbound link,incoming link | 外部回指链接 |
K-Means | K-均值 |
list comprehension | 列表推导式 |
observation | 观测数据,观测值 |
similarity | 相似度,相似性 |
vertical search engine | 垂直搜索引擎 |
前言
本书的目的是要带领超越以数据库为后端的简单应用系统,并告诉你如何利用自己和他人每天搜集到的信息来编写更为智能的程序。
第二章
-
搜集偏好
我们要做的第一件事情,是寻找一种表达不同人及其偏好的方法。在python中,达到这一目的的一种非常简单的方法是使用一个嵌套的字典。如果你打算运行本节中的示例,请新建一个名为recommendations.py的文件,并加入如下代码构造一个数据集:
critics = {
'Lisa Rose':{
'Lady in the Water':2.5, 'Snake 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},
'Gene Seymour':{
'Lady in the Water':3.0, 'Snake 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,},
'Michael Phillips':{
'Lady in the Water':2.5, 'Snake on a Plane':3.0,
'Superman Returns':3.5, 'The Night Listener':4.0},
'Claudia Puig':{
'Snake 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},
'Mick LaSalle':{
'Lady in the Water':3.0, 'Snake 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},
'Jack Matthews':{
'Lady in the Water':3.0, 'Snake on a Plane':4.0,
'The Night Listener':3.0, 'Superman Returns':5.0, 'You, Me and Dupree':3.5},
'Toby':{
'Snake on a Plane':4.5, 'You, Me and Dupree':1.0, 'Superman Returns':4.0}}
- 寻找相似用户
搜集完人们的偏好数据之后,我们须要有一种方法确定人们在品味方面的相似程度,为此,我们可以将每个人与所有其他人进行对比,并计算他们的相似度评价值。有若干种方法可以达到此目的,本节中我们将介绍两套计算相似评价值的体系:欧几里得距离和皮尔逊相似度。
欧几里得距离
它以经过人们一致评价的物品为坐标轴,然后将参与评价的人绘制到图上,并考查他们彼此间的距离远近。
改图显示了处于“偏好空间”中人们的分布状况,Toby在Snakes轴线和Dupree轴线上所标示的数值分别是4.5和1.0.两人在“偏好空间”中的距离越近,他们的兴趣偏好就越相似。因为这张图是二维的,所以在容易时间内你只能看到两项评分,但是这一规则对于更多数据的评分项而言也是同样适用的。
计算出距离值,偏好越相似的人,其距离就越短。不过,我们还需要一个函数,来对偏好越相近的情况给出越大的值。为此,我们可以将函数值加1(这样就可以避免遇到被零整除的错误了),并取其倒数:
>>> 1/(1+sqrt(pow(4.5-4,2)+pow(1-2,2)))
0.4721359549995794
这一新的函数总是返回介于0到1之间的值,返回1则表示两人具有一样的偏好。我们经前述知识结合起来,就可以构造出用来计算相似度的函数了。将下列代码加入recommendations.py:
from math import sqrt
# 返回一个有关 person1 和 person2 的基于距离的相似度评价
def sim_distance(prefs, person1, person2):
#得到 shared_items 的列表
si={
}
for item in prefs[person1]:
if item in prefs[person2]:
si[item] = 1
# 如果没有共同之处,返回 0
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))
皮尔逊相似度
该相关系数是判断两组数据与某一直线拟合程度的一种度量。对应的公式比欧几里得距离评价的计算公式要复杂,但是它的数据不是很规范的时候(比如,影评者对影片的评价总是相对于平均水平偏离很大时),会倾向于给出更好的结果。
为了形象地展示这一方法,我们可以在图上标示出两位评论者的评分情况,如下图所示。Mick LaSalle为《Superman》评了3分,而Gene Seymour则评了5分,所以该影片被定位在图中的(3.5)处。
在采用皮尔逊方法进行评价时,我们可以从图上发现一个值得注意的地方,那就是它修正了“夸大分值(grade inflation)”的情况。在这张图中,虽然Jack Matthews总是倾向于给出比Lisa Rose更高的分支,但最终的直线仍然是拟合的,这是因为他们两者有着相对近似的偏好。如果某人总是倾向于给出比另一个人更高的分值,而两者的分值之差又始终保持一致,则他们依然可能会存在很好的相关性。此前提到过的欧几里得距离评价方法,会因为一个人的评价始终比另一个的更为“严格”(从而导致评价始终相对偏低),而得出两者不想死的结论,即使他们的品味很相似也是如此。而这一行为是否就是我们想要的结果,则却决于具体的应用场景。
皮尔逊相似度评价算法首先会找出两位评论者都曾评价过的物品,然后计算两者的评分总和与平方和,并求得评分的乘积之和。
# 返回 p1 和 p2 的皮尔逊相关系数
def sim_pearson(prefs, p1, p2):
# 得到双方都曾评价过的物品列表
si = {
}
for item in prefs[p1]:
if item in prefs[p2]:si[item] = 1
# 得到 si 列表元素的个数
n = len(si)
# 如果两者没有共同之处,返回 1
if n == 0:return 1
# 对所有偏好求和
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
该函数将返回一个介于-1与1之间的数值。值为1则表明两个人对每一样物品具有着完全一致的评价。
import recommendation
print(recommendation.sim_pearson(recommendation.critics,'Lisa Rose','Gene Seymour') )
为评论者打分
我们已经有了对两个人进行比较的函数,下面我们可以根据指定人员对每个人进行打分,并找出最接近的匹配结果。
# 从反映偏好的字典中返回最为匹配者
# 返回结果的个数和相似度函数均为可选参数
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 recommendation
>>> recommendation.topMatches(recommendation.critics,'Toby',n=3)
[(0.9912407071619299, 'Lisa Rose'), (0.9244734516419049, 'Mick LaSalle'), (0.8934051474415647, 'Claudia Puig')]
根据返回的结果我们了解到,应当阅读Lisa Rose所撰写的评论,因为她的品味与我们的很相似。
推荐物品
找到一位趣味相投的影评者并阅读他所撰写的评论固然不错,但现在我们真正想要的不是这些,而是一份影片的推荐。
为了解决冷启动、奇异值等问题,我们需要通过一个经过加权的评价值来为影片打分,评论者的评分结果因此而形成了先后的排名。为此,我们需要取得所有其他评论者的评价结果,借此得到相似度后,再乘以他们为每部影片所给的评价值。
以S.x打头的列给出了乘以评价值之后的相似度。如此一来,相比于与我们不想近的人,那些与我们相近的人将会对整体评价值拥有更多的贡献。总计一行给出了所有加权评价值的总和。
一部受更多人评论的影片会对结果产生更大的影响。为了修正这一问题,我们需要除以表中名为Sim.Sum的那一行,它代表了所有对这部电影有过评论的评论者的相似度之和。
# 利用所有他人评价值的加权平均,为某人提供建议
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)
# setdefault() 函数: 如果键不已经存在于字典中,将会添加键并将值设为默认值。
# dict.setdefault(key, default=None)
# key -- 查找的键值。
# default -- 键不存在时,设置的默认键值。
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
上述代码循环遍历所有位于字典prefs中的其他人。针对每一次循环,它会计算由person参数所指定的人员与这些人的相似度。然后它会循环遍历所有打过分的项。
用每一项的评价值乘以相似度,并将所得乘以积累加起来。最后,我们将每个总计值除以相似度之和,借此对评价值进行归一化处理,然后返回一个经过排序的结果。
>>> import recommendation
>>> recommendation.getRecommendations(recommendation.critics,'Toby')
[(3.3477895267131017, 'The Night Listener'), (2.8325499182641614, 'Lady in the Water'), (2.530980703765565, 'Just My Luck')]
现在,我们已经建立起了一个完整的推荐系统,它适用于任何类型的商品或网络链接。我们所要做的全部事情就是:建立一个涉及人员、物品和评价值字典,然后就可以借此来为任何人提供建议了。
匹配商品
只需将之前人对物品的评分矩阵转换为物品对人的评分矩阵
#这个函数就是将字典里面的人员和物品对调
def transformPrefs(prefs):
result = {
}
for person in prefs:
for item in prefs[person]:
result.setdefault(item, {
})
#将物品和人员对调
result[item][person] = prefs[person][item]
return result
现在,调用之前用过的topMatches函数,可以得到某个影片相近的影片
>>> import recommendation
>>> movies=recommendation.transformPrefs(recommendation.critics)
>>> recommendation.topMatches(movies,'Snake on a Plane')
[(0.7637626158259785, 'Lady in the Water'), (0.11180339887498941, 'Superman Returns'), (-0.3333333333333333, 'Just My Luck'), (-0.5663521139548527, 'The Night Listener'), (-0.6454972243679047, 'You, Me and Dupree')]
在本例中,实际存在着一些相关评论值为负的情况,这表明存在不喜欢。
上面我们示范了为某部影片提供相关的推荐,不仅如此,我们设置还可以为影片推荐评论者。例如,也许我们正在考虑邀请谁和自己一起参加某部影片的首映式。
>>> recommendation.getRecommendations(movies,'Just My Luck')
[(4.0, 'Michael Phillips'), (3.0, 'Jack Matthews')]
>>>
构建一个基于del.icio.us的链接推荐系统
由于这部分的api在国内不能使用,pydelicious模块也不能导入。也没有看到有很好的解决方案,所以只能跳过。
pydelicious.py
"""Library to access del.icio.us data via Python.
:examples:
Using the API class directly:
>>> a = pydelicious.apiNew('user', 'passwd')
>>> # or:
>>> a = DeliciousAPI('user', 'passwd')
>>> a.tags_get() # Same as:
>>> a.request('tags/get', )
Or by calling the 'convenience' methods on the module.
- def add(user, passwd, url, description, tags = "