群体智慧编程:基于用户推荐算法和基于物品推荐算法
《群体智慧编程》第二章主要讲以下步骤:
- 搜集偏好,即收集数据
- 寻找相近的用户,通过使用欧几里得距离 、皮尔逊相关度评价以及其他相似度度量方法
- 为用户A推荐没有阅读过的物品,通过对相似用户的物品评价值加权得到用户A的大概评价值,根据排序向A推荐(这是基于用户的推荐算法)
- 商品匹配,即找到相似的商品。通过查看哪些人喜欢某一特定物品,以及这些人喜欢哪些其他物品来决定相似度,这各算法需要产生一个整体的物品相似度的表(这是基于物品的推荐算法)
总结:以上1-4的方法针对拥有几百万,几千万,几十亿的大型网站,其计算复杂度很大,计算速度难以容忍,所以可以采用个“基于用户的协作型过滤”(user-based collaborative filtering)技术以及“基于物品的协作型过滤”(item-based collaborative filtering),一般基于物品的协作型过滤比较稳定,而基于用户的协作型过滤比较适合规模较小变化非常平凡的内存数据集。
整体代码如下:
from math import sqrt
# 一个涉及影评及其他队几部影片评分情况的字典
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}
}
# 返回一个有关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_squaress = sum(pow(prefs[person1][item] - prefs[person2][item], 2)
for item in prefs[person1] if item in prefs[person2])
return 1 / (sqrt(sum_of_squaress) + 1)
# 返回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:
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
# 从反映偏好的字典中返回最为匹配者
# 返回结果的个数和相似度函数均为可选参数
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
def calculateSimilarItems(prefs, n=3):
# 建立字典,以给出与这些物品最为相近的所有其他物品
result = {}
# 以物品为中心对偏好矩阵实施倒置处理
itemPrefs = transformPrefs(prefs)
c = 0
for item in itemPrefs:
# 针对大数据集更新状态变量
c += 1
if c % 100 == 0:
print("%d / %d" % (c, len(itemPrefs)))
# 寻找最为相近的物品
scores = topMatches(itemPrefs, item, n=n, similarity=sim_distance)
result[item] = scores
return result
def getRecommendedItems(prefs, itemMatch, user):
userRatings = prefs[user]
scores = {}
totalIsm = {}
# 循环遍历由当前用户评分的物品
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
# 全部相似度之和
totalIsm.setdefault(item2, 0)
totalIsm[item2] += similarity
# 将每个合计值除以加权和,求出平均值
rankings = [(score / totalIsm[item], item) for item, score in scores.items()]
# 将按最高值到最低值的顺序,返回评分结果
rankings.sort()
rankings.reverse()
return rankings
def loadMovieLens(path='E:\Project\ making_recommendations\data'):
# 获取影片标题
movies = {}
for line in open(path + '/u.item', encoding='ISO-8859-1'):
(id, title) = line.split('|')[0:2]
movies[id] = 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
# print(sim_distance(critics, 'Lisa Rose', 'Gene Seymour'))
# print(sim_pearson(critics, 'Lisa Rose', 'Gene Seymour'))
# print(topMatches(critics, 'Toby', n=4))
# print("======================================================")
# print(getRecommendations(critics, 'Toby', similarity=sim_distance))
# itemsim = calculateSimilarItems(critics)
# print(itemsim)
# print(getRecommendedItems(critics, itemsim, 'Toby'))
prefs = loadMovieLens()
# print(prefs['100'])
print("===============================")
itemsim = calculateSimilarItems(prefs, n=5)
print(getRecommendedItems(prefs, itemsim, '87')[0:30])