• 使用Python实现Slope One算法
    • 第一步
    • 第二步
    • 第三步
    • 第四步
    • 加权的Slope One算法:推荐逻辑的实现
    • MovieLens数据集

    使用Python实现Slope One算法

    我们将沿用第二章中编写的Python类,重复的代码我不在这里赘述。输入的数据是这样的:

    1. users2 = {"Amy": {"Taylor Swift": 4, "PSY": 3, "Whitney Houston": 4},
    2. "Ben": {"Taylor Swift": 5, "PSY": 2},
    3. "Clara": {"PSY": 3.5, "Whitney Houston": 4},
    4. "Daisy": {"Taylor Swift": 5, "Whitney Houston": 3}}

    我们先来计算两两物品之间的差异,公式是:

    使用Python实现Slope One算法 - 图1

    计算后的输出结果应该如下表所示:

    使用Python实现Slope One算法 - 图2

    括号中的数值表示同时给这两个歌手评过分的用户数。

    第一步

    1. def computeDeviations(self):
    2. # 获取每位用户的评分数据
    3. for ratings in self.data.values():

    self.data是一个Python字典,它的values()方法可以获取所有键的值。比如上述代码在第一次迭代时,ratings变量的值为{“Taylor Swift”: 4, “PSY”: 3, “Whitney Houston”: 4}。

    第二步

    1. def computeDeviations(self):
    2. # 获取每位用户的评分数据
    3. for ratings in self.data.values():
    4. # 对于该用户的每个评分项(歌手、分数)
    5. for (item, rating) in ratings.items():
    6. self.frequencies.setdefault(item, {})
    7. self.deviations.setdefault(item, {})

    在这个类的初始化方法中,我们需要对self.frequencies和self.deviations进行赋值:

    1. def __init__(self, data, k=1, metric='pearson', n=5):
    2. ...
    3. # 以下变量将用于Slope One算法
    4. self.frequencies = {}
    5. self.deviations = {}

    Python字典的setdefault()方法接受两个参数,它的作用是:如果字典中不包含指定的键,则将其设为默认值;若存在,则返回其对应的值。

    第三步

    1. def computeDeviations(self):
    2. # 获取每位用户的评分数据
    3. for ratings in self.data.values():
    4. # 对于该用户的每个评分项(歌手、分数)
    5. for (item, rating) in ratings.items():
    6. self.frequencies.setdefault(item, {})
    7. self.deviations.setdefault(item, {})
    8. # 再次遍历该用户的每个评分项
    9. for (item2, rating2) in ratings.items():
    10. if item != item2:
    11. # 将评分的差异保存到变量中
    12. self.frequencies[item].setdefault(item2, 0)
    13. self.deviations[item].setdefault(item2, 0.0)
    14. self.frequencies[item][item2] += 1
    15. self.deviations[item][item2] += rating - rating2

    还是用{“Taylor Swift”: 4, “PSY”: 3, “Whitney Houston”: 4}举例,在第一次遍历中,外层循环item = “Taylor Swift”,rating = 4;内层循环item2 = “PSY”,rating2 = 3,因此最后一行代码是对self.deviations[“Taylor Swift”][“PSY”]做+1的操作。

    第四步

    最后,我们便可计算出差异值:

    1. def computeDeviations(self):
    2. # 获取每位用户的评分数据
    3. for ratings in self.data.values():
    4. # 对于该用户的每个评分项(歌手、分数)
    5. for (item, rating) in ratings.items():
    6. self.frequencies.setdefault(item, {})
    7. self.deviations.setdefault(item, {})
    8. # 再次遍历该用户的每个评分项
    9. for (item2, rating2) in ratings.items():
    10. if item != item2:
    11. # 将评分的差异保存到变量中
    12. self.frequencies[item].setdefault(item2, 0)
    13. self.deviations[item].setdefault(item2, 0.0)
    14. self.frequencies[item][item2] += 1
    15. self.deviations[item][item2] += rating - rating2
    16. for (item, ratings) in self.deviations.items():
    17. for item2 in ratings:
    18. ratings[item2] /= self.frequencies[item][item2]

    完成了!仅仅用了18代码我们就实现了这个公式:

    使用Python实现Slope One算法 - 图3

    让我们测试一下:

    1. >>> r = recommender(users2)
    2. >>> r.computeDeviations()
    3. >>> r.deviations
    4. {'PSY': {'Taylor Swift': -2.0, 'Whitney Houston': -0.75}, 'Taylor Swift': {'PSY': 2.0, 'Whitney Houston': 1.0}, 'Whitney Houston': {'PSY': 0.75, 'Taylor Swift': -1.0}}

    结果和我们之前手工计算的一致:

    使用Python实现Slope One算法 - 图4

    感谢Bryan O’Sullivan,这里用Python实现的Slope One算法正是基于他的成果。

    使用Python实现Slope One算法 - 图5

    加权的Slope One算法:推荐逻辑的实现

    使用Python实现Slope One算法 - 图6

    1. def slopeOneRecommendations(self, userRatings):
    2. recommendations = {}
    3. frequencies = {}
    4. # 遍历目标用户的评分项(歌手、分数)
    5. for (userItem, userRating) in userRatings.items():
    6. # 对目标用户未评价的歌手进行计算
    7. for (diffItem, diffRatings) in self.deviations.items():
    8. if diffItem not in userRatings and userItem in self.deviations[diffItem]:
    9. freq = self.frequencies[diffItem][userItem]
    10. recommendations.setdefault(diffItem, 0.0)
    11. frequencies.setdefault(diffItem, 0)
    12. # 分子
    13. recommendations[diffItem] += (diffRatings[userItem] + userRating) * freq
    14. # 分母
    15. frequencies[diffItem] += freq
    16. recommendations = [(k, v / frequencies[k]) for (k, v) in recommendations.items()]
    17. # 排序并返回
    18. recommendations.sort(key=lambda artistTuple: artistTuple[1], reverse=True)
    19. return recommendations
    20. >>> r.slopeOneRecommendations(users2['Ben'])
    21. [('Whitney Houston', 3.375)]

    MovieLens数据集

    让我们在另一个数据集上尝试一下Slope One算法。

    MovieLens数据集是由明尼苏达州大学的GroupLens研究项目收集的,是用户对电影的评分。这个数据集可以在www.grouplens.org下载,有三种大小,这里我使用的是最小的那个,包含了943位用户对1682部电影的评价,约10万条记录。

    我们一起来测试一下:

    1. >>> r = recommender(0)
    2. >>> r.loadMovieLens('/Users/raz/Downloads/ml-100k/')
    3. 102625
    4. >>> r.computeDeviations() # 大约需要50秒
    5. >>> r.slopeOneRecommendations(r.data['25'])
    6. [('Aiqing wansui (1994)', 5.674418604651163), ('Boys, Les (1997)', 5.523076923076923), ...]

    作业

    1. 看看Slope One的推荐结果是否靠谱:对数据集中的10部电影进行评分,得到的推荐结果是否是你喜欢的电影呢?
    2. 实现修正的余弦相似度算法,比较一下两者的运算效率。
    3. (较难)我的笔记本电脑有8G内存,在尝试用Slope One计算图书漂流站数据集时报内存溢出了。那个数据集中有27万本书,因此需要保存超过7300万条记录的Python字典。这个字典的数据是否很稀疏呢?修改算法,让它能够处理更多数据吧。

    使用Python实现Slope One算法 - 图7

    祝贺大家学完第三章了