系统过滤
两种推荐算法(基于用户和基于物品的协同过滤)。
基于用户的协同过滤
协同过滤(Collaborative filtering)
一个协作型过滤算法通常的做法是对一大群人进行搜索,并从中找出与我们品味相近的一小群人。算法会对这些人所偏爱的其他内容进行考察,并将它们组合起来构造出一个经过排名的推荐列表。
测试数据集
为了演示一些算法,我们需要从这个网站上下载一份数据集。这里因为是作为演示使用,所以只需要下载ml-latest-small.zip这一份简单的数据集就可以了。
相似度计算
在有了数据之后,我们需要有一种方法来确定人们在品味方面的相似程度。为此,我 们可以将每个人与所有其他人进行对比,并计算他们的相似度评价值。
欧几里德距离评价(Euclidean Distance Score) 欧几里德距离是指多维空间中两点间的距离,这是一种用直尺测量出来的距离。如果我们将两个点分别记作$(p_1, p_2, p_3, p_4, …)$和$(q_1, q_2, q_3, q_4, …)$,则欧几里德距离的计算公式为:
$$ \sqrt{((p_1-q_1)^2+(p_2-q_2)^2+…+(p_n-q_n)^2)} = \sqrt{(\sum_{i=1}^n(p_i-q_i)^2)} $$
创建 recommendations.py。
用代码表示:
from math import sqrt
# 返回一个有关 person1 与 person2 的基于距离的相似度评价
def sim_distance(prefs, person1, person2):
# 得到 shared_items 的列表
shared_items = {}
for item in prefs[person1]:
if item in prefs[person2]:
shared_items[item] = 1
# 如果两者没有共同之处,则返回 0
if len(shared_items) == 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))
欧几里德距离计算公式可以计算出距离值,偏好越相似的人,其距离就越短。不过,我们还需要一个函数,来对偏好越相近的情况给出越大的值,为此,我们可以将函数的值加 1(这样就可以避免遇到被 0 整除的错误了),并取其倒数,入上面代码最后一行所示。
好了,现在我们需要先将数据读取进来,在 recommendations.py 中添加如下代码:
def loadMovieLens(path='data'):
movies = {}
for line in open(path + '/movies.csv'):
(id, title) = line.split(',')[0:2]
movies[id] = title
prefs = {}
for line in open(path+'/ratings.csv'):
(user, movieid, rating, ts) = line.split(',')
prefs.setdefault(user, {})
prefs[user][movies[movieid]] = float(rating)
return prefs
在 ipython 交互环境中:
>>> import recommendations
>>> prefs = recommendations.loadMovieLens()
>>> # 比较 10 号用户 和 20 号用户的相似度评价
>>> recommendations.sim_distance(prefs, '10', '20')
0.23371479611805132
皮尔逊相关系数(Pearson Correlation Coefficient) 皮尔逊相关系数是一种度量两个变量间相关程度的方法,它是一个介于 1 和 -1 之间的值,其中,1 表示变量完全正相关,0 表示无关, -1 则表示完全负相关(一个变量的值越大,则另一个变量的值反而会越小)。
计算公式:
$$ r = \frac{\sum{XY}-\frac{\sum X \sum Y}{N}}{\sqrt{(\sum X^2 - \frac{(\sum X)^2}{N})(\sum Y^2 - \frac{(\sum Y)^2}{N})}} $$
在 recommendations.py 中添加如下代码:
def sim_pearson(prefs, person1, person2):
# 得到双方都曾评价过的物品列表
shared_items = {}
for item in prefs[person1]:
if item in prefs[person2]:
shared_items[item] = 1
# 得到物品列表元素的个数
n = len(shared_items)
# 如果两者没有共同元素,则返回0
if n == 0:
return 0
# 计算 person1 和 person2 的皮尔逊相关系数
sumxy = sum([prefs[person1][item] * prefs[person2][item]
for item in shared_items])
sumx = sum([prefs[person1][item] for item in shared_items])
sumy = sum([prefs[person2][item] for item in shared_items])
sumx2 = sum([prefs[person1][item] ** 2 for item in shared_items])
sumy2 = sum([prefs[person2][item] ** 2 for item in shared_items])
zahler = sumxy - (sumx * sumy) / n
nenner = sqrt((sumx2 - (sumx ** 2) / n) * (sumy2 - (sumy ** 2) / n))
if nenner == 0:
return 0
r = zahler / nenner
return r
在 ipython 交互环境中:
>>> reload(recommendations)
>>> prefs = recommendations.loadMovieLens()
>>> recommendations.sim_pearson(prefs, '10', '20')
0.4908806936738162
可以看到和上面使用欧几里德距离评价计算出来的相似度是不一样的。
关于该使用哪一种相似性度量方法 除了这两种计算相似度的方法,实际上还有许多别的计算相似度的算法,例如Tanimoto 分值。使用哪一种方法,完全取决于具体的应用。
下面的代码中,将使用一个通用的相似性函数来计算相似度,只要它满足以下条件:拥有同样的函数签名,以一个浮点数作为返回值,其数值越大代表相似度越大。