如果想了解商品、影片或网站的推荐性信息,最没有技术含量的方法是向朋友们询问,其中一部分人的品味会比其他人高一些,通过观察这些人是否通常也和我们一样喜欢同样的东西,可以逐步对这些情况有所了解。不过随着选择越来越多,想要通过询问一小群人来确定我们想要的东西,将会变得越来越不实际。因为他们可能并不了解所有的选择。这就是为什么人们要发展出一套被称作协作型过滤(collaborative filteriing)的技术。
一个协作型过滤算法通常是对一大群人进行搜索,并从中找到与我们品味相近的一小群人。算法会对这些人所偏爱的其他内容进行考察,并从中找出与我们品味相近的一小群人。算法会对这些人所偏爱的其他内容进行考察,并将他们组合起来构造出一个经过排名的推荐列表。有不同的方法可以帮助我们确定那些人与自己品味相近,并将他们的选择组合成列表。
(二)搜集偏好 Collecting Perferences
我们要做的第一件事情,是寻找一种表达不同人及其偏好的方法。在python中,达到这一目的的一种非常简单的方法是使用一个嵌套的字典。我们新建一个名为recommendations.py的文件,并加入如下代码来构造一个数据集:
#一个涉及影评者及其对几部影片评分情况的字典
- 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},
- 'Gene Seymour':{'Lady in the Water':3.0,'Snakes on a Plane':3.5,
- 'Just My Luck':1.5,'Superman Returns':5.0,
- 'The Nighr Listener':3.0,'You,Me and Dupree':3.5},
- 'Michael Phillips':{'Lady in the Water':2.5,'Snakes on a Plane':3.0,
- 'Superman Returns':3.5,'The Nighr Listener':4.0},
- '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},
- '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},
- '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},
- 'Toby':{'Snakes on a Plane':4.5,'You,Me and Dupree':1.0,
- 'Superman Returns':4.0}}
本章中,我们将以交互式方式使用python,因此,应该先将recommendations.py保存起来,以便python的交互解释程序能够读取到它。我们也可以将文件保存在 python/Lib 目录下,
不过最为简单的作法,是在与我们保存文件的同一目录下启动python解释程序。
上述字典使用从1到5的评分,以此来体现包括本人在内的每位影评人对某一给定影片的喜爱程度。不管偏好是如何表达的,我们需要一种方法来将它们对应到数字。加入我们正在架构一个购物网站,不妨用数字1来表示有人过去曾购买过某件商品,用数字0表示未曾购买过任何商品。而对于一个新闻故事的投票网站,我们可以分别用数字-1,0和1来表达“不喜欢”,“没有投票”,“喜欢”,如表2-1所示:
表2-1:从用户行为到相应评价值的可能对应关系
音乐会门票 |
| 在线购物 |
| 网站推荐者 |
|
已购买 | 1 | 已购买 | 2 | 喜欢 | 1 |
未购买 | 0 | 已浏览 | 1 | 未投票 | 0 |
|
| 未购买 | 0 | 不喜欢 | -1 |
- from recommendations import critics
- #从文件recommendations.py中导入字典critics
- critics['Lisa Rose']['Lady in the Water']
- #查询Lisa Rose对影片“Lady in the Water”的评分
- critics['Toby']['Snakes on a Plane']=4.5
- #增加Toby对影片“Snakes on a Plane”的评分
- critics['Toby']
- #查询Toby的所有信息
尽管可以将相当数量的人员偏好信息置于字典内(即内存中),但对于一个规模巨大的数据及而言,也许我们还是会希望将其存入数据库中。
(三)寻找相近的用户 Finding Similar Users
搜集完人们的偏好数据后,我们需要有一种方法来确定人们在品位方面的相似程度。为此,我们可以将每个人与所有其他人进行对比,并计算他们的相似度评价值。有若干种方法可以达到此目的,下面是两套计算相似度评价值的体系:欧几里得距离和皮尔逊相关度。
欧几里得度量定义欧几里得空间中点 x = (x1,...,xn) 和 y = (y1,...,yn) 之间的距离为
计算相似度评价值的一个非常简单的方法是使用欧几里得距离评价方法。它以经过人们一致评价的物品作为坐标轴,然后将参与评价的人绘制到图上,并考察他们彼此之间的距离远近,如图2-1所示:
图2-1: 处于“偏好空间”中的人们
该图显示了处于“偏好空间”中人们的分布情况。Toby在Snake轴线和Dupree周线上所标示的数值分别是4.5和1.0,两人在“偏好空间”中的距离越近,他们的兴趣偏好就越相似。因为这张图是二维的,所以在同一时间内只能看到两项评分,但这一规则对于更多数量的评分项而言也是实用的。
为了计算图上Toby和LaSalle之间的距离,我们可以计算出每一轴向上的差值,求平方后再相加,最后对总和取平方根。在Python中,我们可以用函数pow(n,2)对某数求平方,并使用sqrt函数求平方根:
- <div style="text-align: left;"><span style="font-family: Arial, Helvetica, sans-serif;">from math import sqrt</span></div><div style="text-align: left;"><span style="font-family: Arial, Helvetica, sans-serif;">sqrt(pow(4.5-4,2)+pow(1-2,2))</span></div>
- <span style="font-size:12px;">import math
- math.pow(x,y)</span>
注意:pow() 通过内置的方法直接调用,内置方法会把参数作为整型,而 math 模块则会把参数转换为 float。
2. sqrt()方法返回 x (x>0) 的平方根; math 模块中 sqrt 函数只能进行浮点数的运算
负数的平方根是虚数(以及复数,即实数和虚数之和),这个需要一个专门的函数 cmath (complex math,复数)的模块做处理:
- import cmath
- cmath.sqrt(-1)
结果为1j
)
上述算式可以计算出距离值,偏好越相似的人,其距离就越短。我们还需要一个函数来对偏好越相近的情况给出越大的值。为此,我们可以将函数值加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):
- #prefs是不同人的偏好的字典,person1和person2分别为字典prefs的两个关键值
- #得到shared_items的列表
- si={} #建立一个字典si,其中关键值为item,在此例中即为各个被评价的影片
- 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))
我们调用该函数,分别传入两个人的名字,并计算出相似度的评价值。
- from imp import reload
- reload(recommendations)
- recommendations.sim_distance(recommendations.critics,'Lisa Rose','Gene Seymour')
(注:reload():
在python中,每一个以 .py结尾的Python文件都是一个模块。其他的文件可以通过导入一个模块来读取该模块的内容。导入从本质上来讲,就是载入另一个文件,并能够读取那个文件的内容。一个模块的内容通过这样的属性能够被外部世界使用。
这种基于模块的方式使模块变成了Python程序架构的一个核心概念。更大的程序往往以多个模块文件的形式出现,并且导入了其他模块文件的工具。其中的一个模块文件被设计成主文件,或叫做顶层文件(就是那个启动后能够运行整个程序的文件)。
默认情况下,模块在第一次被导入之后,其他的导入都不再有效。如果此时在另一个窗口中改变并保存了模块的源代码文件,也无法更新该模块。这样设计的原因在于,导入是一个开销很大的操作(导入必须找到文件,将其编译成字节码,并且运行代码),以至于每个文件、每个程序运行不能够重复多于一次。
Python2 中可以直接使用reload(module)重载模块。
Pyhton3中需要使用如下两种方式:
方式(1)
- from imp
- imp.reload(module)
- from imp import reload
- reload(module)
>>> from imp import reload
>>> reload(recommendations)
Traceback (most recent call last):
File "<pyshell#86>", line 1, in <module>
reload(recommendations)
NameError: name 'recommendations' is not defined
>>> from imp import reload
>>> import recommendations
>>> reload(recommendations))
· 当相关系数为0时,X和Y两变量无关系。
· 当X的值增大,Y也增大,正相关关系,相关系数在0.00与1.00之间
· 当X的值减小,Y也减小,正相关关系,相关系数在0.00与1.00之间
· 当X的值增大,Y减小,负相关关系,相关系数在-1.00与0.00之间
当X的值减小,Y增大,负相关关系,相关系数在-1.00与0.00之间
- #返回p1和p2的皮尔逊相关系数
- def sim_pearson(prefs,p1,p2):
- #得到双方都曾评价过的物品列表
- si={}
- for item in prefs[p1]:
- if item in prefs[p2]:
- si[item]=1
- #得到列元素的个数
- n=len(si)
- #如果两者没有共同之处,返回1
- if n==0:
- <span style="white-space:pre"> </span>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时,两个人对同一样物品均有着一致的评价。与距离度量法不同,此处我们无需为达到正确的比率而对这一数值进行变换。以下代码求得图2-3中的相关评价值:
- reload(recommendations)
- print(recommendations.sim_person(recommendations.critics,'Lisa Rose','Gene Seymour'))
(五)应该选用哪一种相似性度量方法 Which Similarity Metic Should You Use?
曼哈顿距离来源于城市区块距离,是将多个维度上的距离进行求和后的结果,即当上面的明氏距离中p=1时得到的距离度量公式,如下: