转载请注明出处:http://blog.csdn.net/gamer_gyt
博主微博:http://weibo.com/234654758
Github:https://github.com/thinkgamer
公众号:搜索与推荐Wiki
个人网站:http://thinkgamer.github.io
该篇文章主要介绍Slope One算法。Slope One 算法是由 Daniel Lemire 教授在 2005 年提出的一个 Item-Based 的协同过滤推荐算法。和其它类似算法相比, 它的最大优点在于算法很简单, 易于实现, 执行效率高, 同时推荐的准确性相对较高。
经典的ItemCF的问题
经典的基于物品推荐,相似度矩阵计算无法实时更新,整个过程都是离线计算的,而且还有另一个问题,相似度计算时没有考虑相似度的置信问题。例如,两个物品,他们都被同一个用户喜欢了,且只被这一个用户喜欢了,那么余弦相似度计算的结果是 1,这个 1 在最后汇总计算推荐分数时,对结果的影响却最大。
Slope One 算法针对这些问题有很好的改进。不过 Slope One 算法专门针对评分矩阵,不适用于行为矩阵。
Slope One算法过程
Slope One 算法是基于不同物品之间的评分差的线性算法,预测用户对物品评分的个性化算法。
Slope算法主要分为3步
- 计算物品之间的评分差的均值,记为物品间的评分偏差 (两物品同时被评分)
R ( i , j ) = ∑ u ∈ N ( i ) ⋂ N ( j ) ( r u i − r u j ) ∣ N ( i ) ⋂ N ( j ) ∣ R(i,j) = \frac{ \sum_{ u \in N(i)\bigcap N(j) } (r_{ui} - r_{uj}) }{ | N(i) \bigcap N(j) | } R(i,j)=∣N(i)⋂N(j)∣∑u∈N(i)⋂N(j)(rui−ruj)
( r_ui - r_uj ) 表示评分的差,这里需要注意的是j相对i的评分偏差是 r_ui - r_uj ,如果是i相对j的评分偏差则是 r_uj - r _ui,两 者是互为相反数的关系。
其中:
- r_ui :用户u对物品i的评分
- r_uj :用户u对物品j的评分
- N(i) :物品i评过分的用户
- N(j) :物品j评过分的用户
- N(i) 交 N(j) :表示同时对物品i 和物品j评过分的用户数。
- 根据物品间的评分偏差和用户的历史评分,预测用户对未评分的物品的评分。
p u j = ∑ i ∈ N ( u ) ∣ N ( i ) ⋂ N ( j ) ∣ ( r u i − R ( i , j ) ) ∑ i ∈ N ( u ) ∣ N ( i ) ⋂ N ( j ) ∣ p_{uj} = \frac{ \sum_{i \in N(u)} |N(i) \bigcap N(j) |(r_{ui} - R(i,j)) }{ \sum_{i \in N(u)}|N(i) \bigcap N(j)| } puj=∑i∈N(u)∣N(i)⋂N(j)∣∑i∈N(u)∣N(i)⋂N(j)∣(rui−R(i,j))
其中:
- N(u) :用户u评过分的物品
- 将预测评分进行排序,取Top N对应的物品推荐给用户
实例说明
例如现在有一份评分数据,表示用户对电影的评分:
- | a | b | c | d | e |
---|---|---|---|---|---|
U1 | 2 | 3 | 3 | 4 | |
U2 | 4 | 2 | 3 | 3 | |
U3 | 4 | 2 | 3 | 2 | |
U4 | 3 | 5 | 4 | 3 |
现在我们来预测预测每个用户对未评分电影的评分。
Step1: 计算物品之间的评分偏差,以U1为例:
R
(
a
,
b
)
=
(
2
−
3
)
+
(
4
−
2
)
2
=
0.5
R(a,b) = \frac{ (2-3) + (4-2) }{ 2 } = 0.5
R(a,b)=2(2−3)+(4−2)=0.5
R
(
a
,
c
)
=
(
2
−
3
)
+
(
4
−
3
)
+
(
3
−
5
)
3
=
−
0.67
R(a,c) = \frac{ (2-3) + (4-3) +(3-5) }{ 3 } = -0.67
R(a,c)=3(2−3)+(4−3)+(3−5)=−0.67
R
(
a
,
d
)
=
(
2
−
4
)
+
(
3
−
4
)
2
=
−
1.5
R(a,d) = \frac{ (2-4) + (3-4) }{ 2 } = -1.5
R(a,d)=2(2−4)+(3−4)=−1.5
R
(
a
,
e
)
=
(
4
−
2
)
+
(
3
−
3
)
2
=
1
R(a,e) = \frac{ (4-2) + (3-3) }{ 2 } = 1
R(a,e)=2(4−2)+(3−3)=1
同理可以计算出电影b,c,d,e与其他电影的评分偏差。
Step2: 计算用户对未评分物品的可能评分(为了方便计算,这里以U2为例)
由上表可知,用户U2 对电影a没有评分,这里计算用户U2对电影a的评分。
p u 2 , a = 2 ∗ ( 4 − 0.5 ) + 3 ∗ ( 2 − ( − 0.67 ) ) + 2 ∗ ( 3 − ( − 1.5 ) ) + 2 ∗ ( 3 − 1 ) ) 2 + 3 + 2 + 2 = 3.11 p_{u_2,a} = \frac{2 * (4-0.5) +3 * (2-(-0.67)) + 2 * (3-(-1.5) ) + 2 * (3-1)) }{ 2+3+2+2} = 3.11 pu2,a=2+3+2+22∗(4−0.5)+3∗(2−(−0.67))+2∗(3−(−1.5))+2∗(3−1))=3.11
Step3: 评分排序
由于给定样例中,U2只对a没有评过分,所以这里不需要进行排序,正常的话,按分数进行倒排就行。
代码实现
这里采用Python实现,在实现过程中并没有考虑算法的复杂度问题。
加载数据
def loadData(self):
user_rate = {
"U1": {"a": 2, "b": 3, "c": 3, "d": 4},
"U2": {"b": 4, "c": 2, "d": 3, "e": 3},
"U3": {"a": 4, "b": 2, "c": 3, "e": 2},
"U4": {"a": 3, "c": 5, "d": 4, "e": 3}
}
item_rate = {
"a": {"U1": 2, "U3": 4, "U4": 3},
"b": {"U1": 3, "U2": 4, "U3": 2},
"c": {"U1": 3, "U2": 2, "U3": 3, "U4": 5},
"d": {"U1": 4, "U2": 3, "U4": 4},
"e": {"U2": 3, "U3": 2, "U4": 3}
}
return user_rate,item_rate
计算物品之间的评分偏差
def cal_item_avg_diff(self):
avgs_dict = {}
for item1 in self.item_rate.keys():
for item2 in self.item_rate.keys():
avg = 0.0
user_count = 0
if item1 != item2:
for user in self.user_rate.keys():
user_rate = self.user_rate[user]
if item1 in user_rate.keys() and item2 in user_rate.keys():
user_count += 1
avg += user_rate[item1] - user_rate[item2]
avg = avg / user_count
avgs_dict.setdefault(item1,{})
avgs_dict[item1][item2] = avg
return avgs_dict
计算预估评分
def item_both_rate_user(self, item1, item2):
count = 0
for user in self.user_rate.keys():
if item1 in self.user_rate[user].keys() and item2 in self.user_rate[user].keys():
count += 1
return count
def predict(self, user, item, avgs_dict):
total = 0.0 # 分子
count = 0 # 分母
for item1 in self.user_rate[user].keys():
num = self.item_both_rate_user(item, item1)
count += num
total += num * (self.user_rate[user][item1] - avgs_dict[item][item1])
return total/count
主函数调用
if __name__ == "__main__":
slope = SlopeOne()
avgs_dict = slope.cal_item_avg_diff()
result = slope.predict("U2", "a", avgs_dict)
print("U2 对 a的预测评分为: %s" % result)
打印结果为:
U2 对 a的预测评分为: 3.111111111111111
和上边我们计算的结果一致。
完整代码在:https://github.com/Thinkgamer/Machine-Learning-With-Python/tree/master/Recommend
Slope One的应用场景
该算法适用于物品更新不频繁,数量相对较稳定并且物品数目明显小于用户数的场景。比较依赖用户的用户行为日志和物品偏好的相关内容。
其优点:
- 算法简单,易于实现,执行效率高;
- 可以发现用户潜在的兴趣爱好;
其缺点:
- 依赖用户行为,存在冷启动问题和稀疏性问题。

