提供推荐
协同过滤算法通常的做法是对一大群人进行搜索,并从中找到与我们品味相近的一小群人,算法会对这些人的偏好的其他内容进行考查,并将它们组合起来构造出一个经过排名的推荐列表。
-
收集偏好
这里如果数据量不大的时候可以直接放入内存中,但是如果数据量过大的时候要放入数据库
-
寻找相近的用户
收集数据完成后,需要计算他们的相似度评价值,常见的两种的方式为:欧几里得距离和皮尔逊相关度
-
关于对于选择哪一种相似度量方法
其实还有Jaccard系数和曼哈顿距离等,其实各种的相似度量的方法对结果影响是很小的。
-
为评分者打分
如果是基于用户的协同过滤的话,这里求的是用户与目标用户的之间的相似度,并按相似度高低排序
如果是基于物品的协同过滤的话,这里求的是物品与目标物品的相似度,并按相似度的高低排序
-
推荐物品
-
考虑到评论者还没对某些影片做评论,而这些影片正是我们喜欢的影片,还有可能就是匹配到某个热衷于某个影片的古怪者,但是所有的评论者都不看好这部影片:解决方案为 可以采用加权得方式对结果进行修正,通过相似度在乘上他们影片得分数值
-
我们可以采用计算总值得方式进行排序得出结果,考虑到一部受更多人评论的影片对结果产生更大的影响,为了修正这个问题,我们需要除以表中所有对这部电影的有关评论的评论者的相似度和
-
-
匹配商品
其实这里是基于物品的协同过滤算法,通过求物品的相似度来基于物品进进行推荐
下面介绍两种协同过滤算法
基于用户的协同过滤算法
收集数据的偏好 — 数据的收集问题
-
寻找相近的用户,需要一种度量方式来确定人们在品味方面的相似度,一般称为相似度评价值。常用的方法有欧几里德距离和皮尔逊相关度。
-
欧几里德距离评价:经过人们一致评价的物品为坐标轴,然后将参与评价的人绘制到坐标轴上,并考察他们的距离。
from math import sqrt # 返回一个关于person1与person2的基于距离的相似度评价 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]]) # 新函数返回总是介于0-1的之间的值,返回1表示两个人具有相同的爱好,这里分母上+1的防止分母为0 return 1 / (1 + sqrt(sum_of_squares))
-
皮尔逊相关度评价:计算公式较为复杂,但是他在数据不是很规范的时候(比如偏差很大的时候),会倾向给出好的结果。采用皮尔逊方法评价时候,它可以修正 “夸大分值”,虽然一个人总是比另一个人的评分高,但是使用皮尔逊相关系数依然可以存在相关性,但是如使用欧几里得算法,得出两个人的不相近的结论。
def sim_pearson(prefs,p1,p2): # Get the list of mutually rated items si={} for item in prefs[p1]: if item in prefs[p2]: si[item]=1 # if they are no ratings in common, return 0 if len(si)==0: return 0 # Sum calculations n=len(si) # Sums of all the preferences sum1=sum([prefs[p1][it] for it in si]) sum2=sum([prefs[p2][it] for it in si]) # Sums of the squares sum1Sq=sum([pow(prefs[p1][it],2) for it in si]) sum2Sq=sum([pow(prefs[p2][it],2) for it in si]) # Sum of the products pSum=sum([prefs[p1][it]*prefs[p2][it] for it in si]) # Calculate r (Pearson score) 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
-
-
为评论者打分
# 从反应偏好的字典种返回最为匹配的者 # 返回的结果的个数和相似度函数均为可选参数 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]
-
推荐物品
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
基于物品的协同过滤算法
基于用户的协同过滤算法对于数量以千的用户数量或者物品规模是可以的,但是对于大型的数据量,其速度会非常慢。
同时,一个销售量为数百万的网站,也许在用户偏好方面上会有很少的重叠,可以让用用户的相似性判断非常的难,在拥有大量的数据的时候,基于物品的协同过滤算法能够得到更好的结果,而且它允许我们将大量的计算任务预先执行,
-
构造物品比较数据集 当用户的基数和评分的数量不是很大的时候,需要频繁的执行该函数,才能使相似度不至于过期,随着用户的数量不断地增长地时候,物品之间地相似度评价值通常会变得很稳定。
def transformPrefs(prefs): result = {} for person in prefs: for item in prefs[person]: result.setdefault(item, {}) # 将物品和人员对调 result[item][person] = prefs[person][item] return result # movies = transformPrefs(critics) def calculateSimilarItems(prefs, n=10): result = {} itemPrefs = transformPrefs(prefs) c = 0 for item in itemPrefs: c += 1 if c % 100 == 0: print("{} / {}".format(c, len(itemPrefs))) scores = topMatches(itemPrefs, item, n=n, similarity=sim_distance) result[item] = scores return result itemsim = calculateSimilarItems(critics)
-
获得推荐
def getRecommendedItems(prefs, itemMatch, user): userRatings = prefs[user] scores = {} totalSim = {} for (item, rating) in userRatings.items(): for (similarity, item2) in itemMatch[item]: if item2 in userRatings: continue scores.setdefault(item2, 0) scores[item2] += similarity * rating totalSim.setdefault(item2, 0) totalSim[item2] += similarity rankings = [(score / totalSim[item], it em) for item, score in scores.items()] rankings.sort() rankings.reverse() return rankings print(getRecommendedItems(critics,itemsim,'Toby'))
使用数据集对算法进行测试
def loadMovieLens(path='/data/movielens'):
movies = {}
for line in open(path + "/u.item"):
(ids, title) = line.split('|')[0:2]
movies[ids] = title
prefs = {}
for line in open(path, '/u.data'):
(user, movieid, rating, ts) = line.split('\t')
prefs.setdefault(user, {})
prefs[user][movies[movieid]] = float(rating)
return prefs
prefs = loadMovieLens()
getRecommendations(prefs,'87')[0:30]
itemsim = calculateSimilarItems(prefs,n=50)
getRecommendedItems(prefs,itemsim,'87')[0:30]
是基于用户进行协同过滤还是基于物品进行协同过滤
针对大数据集地时候,基于物品进行过滤地时候比基于用户进行过滤的更快,不过它要维护物品相似度表的额外开销,
对于稀疏矩阵来说,基于物品的协同过滤要好于基于用户的的,对于密集性的数据集,两者的效果几乎一样。
基于用户的协同过滤算法更加易于实现,通常适合于规模较小的变换很频繁的内存数据集。