协调过滤算法(Collaborative Filtering,CF)
上一篇文章中提到过,推荐系统中常用到的算法包括用户偏好算法、协同过滤算法(item_base,user_base)、关联规则算法、聚类算法、内容相似性算法(content_base)。关联规则中的 Apriori 算法和 FP-growth 算法在之前的文章已经详细介绍过,在这篇文章中,将介绍协同过滤算法(分为两类:基于用户的协同过滤算法、基于物品的协同过滤算法)。
一、算法核心和原理
协同过滤算法核心步骤如下:
1)收集用户偏好;
2)找到相似的用户或物品;
3)计算并推荐。
算法原理:协同过滤算法是比较著名的推荐算法,主要功能是预测和推荐。算法通过对用户历史行为数据的挖掘发现用户的偏好,基于不同的偏好对用户进行群组划分并推荐品味相似的商品。
二、基于物品的协同过滤算法(ItemCF)
基于物品的协同过滤算法通过计算不同用户对不同物品的评分获得物品间的关系,基于物品间的关系对用户进行相似物品的推荐,评分即代表用户对物品的态度和偏好。比如,用户A同时购买了物品x,y,那么说明x,y之间的相关度高,当用户B也购买了物品x时,那么可以预测B也可能买物品y。
2.1 算法流程:
1)构建用户–>物品的倒排(即倒查表);
2)构建物品与物品的共现矩阵;
3)计算物品之间的相似度,即计算相似矩阵;
4)根据用户的历史记录,给用户推荐物品。
(其中,2、3步实际上可以合为一步,相似矩阵为共现矩阵的改进优化。)
2.2 实例
如下表,行表示用户,列表示物品,1 表示用户喜欢该物品
表 1:
用户\物品 | a | b | c | d | e |
---|---|---|---|---|---|
A | 1 | 1 | 1 | ||
B | 1 | 1 | 1 | ||
C | 1 | 1 | |||
D | 1 | 1 | 1 | ||
E | 1 | 1 |
1、构建用户—>物品的倒排
A
:
a
、
b
、
d
A:a、b、d
A:a、b、d
B
:
b
、
c
、
e
B:b、c、e
B:b、c、e
C
:
c
、
d
C:c、d
C:c、d
D
:
b
、
c
、
d
D:b、c、d
D:b、c、d
E
:
a
、
d
E:a、d
E:a、d
#在python中构建的数据格式如下:
{
'A': {'a': '1', 'b': '1', 'd': '1'},
'B': {'b': '1', 'c': '1', 'e': '1'},
'C': {'c': '1', 'd': '1'},
'D': {'b': '1', 'c': '1', 'd': '1'},
'E': {'a': '1', 'd': '1'}
}
2、构建物品与物品的共现矩阵(C)
共现矩阵表示同时喜欢两个物品的用户数,是一个对称矩阵,是由用户—>物品的倒排表计算出来的。
物品 | a | b | c | d | e |
---|---|---|---|---|---|
a | —— | 1 | 0 | 2 | 0 |
b | 1 | —— | 2 | 2 | 1 |
c | 0 | 2 | —— | 2 | 1 |
d | 2 | 2 | 2 | —— | 0 |
e | 0 | 1 | 1 | 0 | —— |
3、计算物品之间的相似度,即计算相似矩阵
设
∣
N
(
i
)
∣
|N(i)|
∣N(i)∣ 表示喜欢物品
i
i
i 的用户数,
∣
N
(
i
)
∩
N
(
j
)
∣
|N(i)\cap N(j)|
∣N(i)∩N(j)∣ 表示同时喜欢物品
i
i
i 、
j
j
j 的用户数,则物品
i
i
i 与物品
j
j
j 的相似度为:
W
i
j
=
∣
N
(
i
)
∩
N
(
j
)
∣
∣
N
(
i
)
∣
W_{ij}=\frac{|N(i)\cap N(j)|}{|N(i)|}
Wij=∣N(i)∣∣N(i)∩N(j)∣
但上式有一个问题,当物品
j
j
j 是一个很热门的物品时,人人都喜欢,那么,
W
i
j
W_{ij}
Wij 就会很接近
1
1
1 ,那上式会让很多物品都和热门物品有一个很大的相似度。所以,需要改进一下公式:
W
i
j
=
∣
N
(
i
)
∩
N
(
j
)
∣
∣
N
(
i
)
∣
⋅
∣
N
(
j
)
∣
W_{ij}=\frac{|N(i)\cap N(j)|}{\sqrt{|N(i)|\cdot|N(j)|}}
Wij=∣N(i)∣⋅∣N(j)∣∣N(i)∩N(j)∣
上式中,分子即为共现矩阵,矩阵
N
N
N (用于计算分母)表示喜欢某物品的用户数(是总的用户数),
矩阵
N
N
N如下:
物品 | a | b | c | d | e |
---|---|---|---|---|---|
用户数 | 2 | 3 | 3 | 4 | 1 |
所以,物品之间的余弦相似矩阵如下(因为该矩阵是对阵的,所以在矩阵右上部分我写出了相似度计算的公式,左下部分写出了对应公式的值):
物品\物品 | a | b | c | d | e |
---|---|---|---|---|---|
a | —— | 1 2 ⋅ 3 = 1 6 \frac{1}{\sqrt{2}\cdot\sqrt{3}}=\frac{1}{\sqrt{6}} 2⋅31=61 | 0 | 2 2 ⋅ 4 = 1 2 \frac{2}{\sqrt{2}\cdot\sqrt{4}}=\frac{1}{\sqrt{2}} 2⋅42=21 | 0 |
b | 1 6 = 0.41 \frac{1}{\sqrt{6}}=0.41 61=0.41 | —— | 2 3 ⋅ 3 = 2 3 \frac{2}{\sqrt{3}\cdot\sqrt{3}}=\frac{2}{3} 3⋅32=32 | 2 3 ⋅ 4 = 1 3 \frac{2}{\sqrt{3}\cdot\sqrt{4}}=\frac{1}{\sqrt{3}} 3⋅42=31 | 1 3 ⋅ 1 = 1 3 \frac{1}{\sqrt{3}\cdot\sqrt{1}}=\frac{1}{\sqrt{3}} 3⋅11=31 |
c | 0 | 2 3 = 0.67 \frac{2}{3}=0.67 32=0.67 | —— | 2 3 ⋅ 4 = 1 3 \frac{2}{\sqrt{3}\cdot\sqrt{4}}=\frac{1}{\sqrt{3}} 3⋅42=31 | 1 3 ⋅ 1 = 1 3 \frac{1}{\sqrt{3}\cdot\sqrt{1}}=\frac{1}{\sqrt{3}} 3⋅11=31 |
d | 1 2 = 0.71 \frac{1}{\sqrt{2}}=0.71 21=0.71 | 1 3 = 0.58 \frac{1}{\sqrt{3}}=0.58 31=0.58 | 1 3 = 0.58 \frac{1}{\sqrt{3}}=0.58 31=0.58 | —— | 0 |
e | 0 | 1 3 = 0.58 \frac{1}{\sqrt{3}}=0.58 31=0.58 | 1 3 = 0.58 \frac{1}{\sqrt{3}}=0.58 31=0.58 | 0 | —— |
4、根据用户的历史记录,给用户推荐物品
推
荐
结
果
=
改
进
的
共
现
矩
阵
∗
评
分
矩
阵
=
相
似
矩
阵
∗
评
分
矩
阵
推荐结果=改进的共现矩阵*评分矩阵=相似矩阵*评分矩阵
推荐结果=改进的共现矩阵∗评分矩阵=相似矩阵∗评分矩阵
ItemCF 通过如下公式计算用户
u
u
u 对一个物品
j
j
j 的兴趣度:
P
u
j
=
∑
i
∈
N
(
u
)
∩
S
(
j
,
k
)
W
j
i
r
u
i
P_{uj}=\sum_{i \in N(u) \cap S(j,k)}W_{ji}r_{ui}
Puj=i∈N(u)∩S(j,k)∑Wjirui
其中,
N
(
u
)
N(u)
N(u) :用户喜欢的物品的集合;
S
(
j
,
k
)
S(j,k)
S(j,k) :和物品
j
j
j 最相似的
k
k
k 个物品的集合;
W
j
i
W_{ji}
Wji :物品
j
j
j 和
i
i
i 的相似度;
r
u
i
r_{ui}
rui :用户
u
u
u 对物品
i
i
i 的兴趣度(对于隐反馈数据集,如果用户
u
u
u 对物品
i
i
i 有过行为,即可令
r
u
i
=
1
r_{ui}=1
rui=1 )
上式的含义是:和用户历史上感兴趣的物品越相似的物品,越有可能在用户的推荐列表中获得比较高的排名。
如:以用户
A
A
A 为例,建立用户对物品的评分矩阵(即
A
A
A 对物品的兴趣度)如下:
物品 | 评分 |
---|---|
a | 1 |
b | 1 |
c | 0 |
d | 1 |
e | 0 |
用户 A A A 已经见过物品 a , b , d a,b,d a,b,d ,并对其进行了评分,所以我们需要选择是推荐物品 c c c 还是物品 e e e 给用户 A A A
推荐结果:
物品 | 兴趣度 |
---|---|
c | 1.25 |
e | 0.58 |
其中,
(
0
,
0.67
,
0
,
0.58
,
0.58
)
⋅
(
1
1
0
1
0
)
=
1.25
(0,0.67,0,0.58,0.58)\cdot \begin{pmatrix} 1\\ 1\\ 0\\ 1\\ 0 \end{pmatrix}=1.25
(0,0.67,0,0.58,0.58)⋅⎝⎜⎜⎜⎜⎛11010⎠⎟⎟⎟⎟⎞=1.25,
(
0
,
0.58
,
0.58
,
0
,
0
)
⋅
(
1
1
0
1
0
)
=
0.58
(0,0.58,0.58,0,0)\cdot \begin{pmatrix} 1\\ 1\\ 0\\ 1\\ 0 \end{pmatrix}=0.58
(0,0.58,0.58,0,0)⋅⎝⎜⎜⎜⎜⎛11010⎠⎟⎟⎟⎟⎞=0.58
则可以看出,用户
A
A
A 对物品
c
c
c 的预测兴趣度(即喜爱程度)为1.25, 对物品
e
e
e 的预测兴趣度(即喜爱程度)为0.58。所以,可向
A
A
A 推荐物品
c
c
c .
二、基于用户的协同过滤算法(UserCF)
基于用户的协同过滤算法与上述过程类似。是通过用户的历史行为数据发现用户对商品或内容的喜欢(如商品购买,内容收藏、评论或分享等)并对这些喜好进行度量和打分。根据不同用户对物品或内容的态度和偏好程度,计算用户之间的关系,在有相同喜好兴趣的用户间进行物品推荐。比如,用户A,B都看过了图书x,y,z,并且给出的评分相似(如都评论了或者点赞了等行为),那么A,B就可以划分为一类群组,就可以将A看过但B没有看过的图书也推荐给B。
下面列出大致过程,就不一一详述了。
还是以如下表一为例:
1、构建倒排(物品—>用户):
a
:
A
,
E
a:A,E
a:A,E
b
:
A
,
B
,
D
b:A,B,D
b:A,B,D
c
:
B
,
C
,
D
c:B,C,D
c:B,C,D
d
:
A
,
C
,
D
,
E
d:A,C,D,E
d:A,C,D,E
e
:
B
e:B
e:B
2、用户与用户的共现矩阵
C
C
C :
3、矩阵
N
N
N (各用户喜欢的物品数):
用户之间的相似矩阵:
4、用户间的相似度:
以用户
A
A
A 为例,该向用户推荐物品
c
c
c 还是物品
e
e
e 呢?
由表一可知,看过物品
c
c
c 的有用户
B
B
B 、
C
C
C 、
D
D
D ;看过物品
e
e
e 的有用户
B
B
B 。
所以,计算用户
A
A
A 对物品
c
c
c 的兴趣度,可以根据用户
A
A
A 与用户
B
B
B 、
C
C
C 、
D
D
D 之间的相似度得到:
P
(
A
,
c
)
=
W
A
B
+
W
A
C
+
W
A
D
=
0.33
+
0.41
+
0.67
P(A,c)=W_{AB}+W_{AC}+W_{AD}=0.33+0.41+0.67
P(A,c)=WAB+WAC+WAD=0.33+0.41+0.67
P
(
A
,
e
)
=
W
A
B
=
0.33
P(A,e)=W_{AB}=0.33
P(A,e)=WAB=0.33
所以,应该向用户
A
A
A 推荐物品
c
c
c 。
- 附:python代码
#/python/Item_CF.py
# -*- coding: UTF-8 -*-
from math import sqrt
import operator
#1.构建用户-->物品的倒排
def loadData(files):
data ={}
for line in files:
user,score,item=line.split(",")
data.setdefault(user,{})
data[user][item]=score
print("----1.用户:物品的倒排----")
print(data)
return data
def loadData2(files):
data={}
for line in files:
user,item,score,timestamp=line.split(",")
data.setdefault(user,{})
data[user][item]=score
print("----1.用户:物品的倒排----")
print(data)
return data
#2.计算
# 2.1 构造物品-->物品的共现矩阵
# 2.2 计算物品与物品的相似矩阵
#(这里采用的是余弦相似度算法计算的物品间的相似度)
def similarity(data):
# 2.1 构造物品:物品的共现矩阵
N={}#喜欢物品i的总人数
C={}#喜欢物品i也喜欢物品j的人数
for user,item in data.items():
for i,score in item.items():
N.setdefault(i,0)
N[i]+=1
C.setdefault(i,{})
for j,scores in item.items():
if j not in i:
C[i].setdefault(j,0)
C[i][j]+=1
print("---2.构造的共现矩阵---")
print ('N:',N)
print ('C:',C)
#2.2 计算物品与物品的相似矩阵
W={}
for i,item in C.items():
W.setdefault(i,{})
for j,item2 in item.items():
W[i].setdefault(j,0)
W[i][j]=C[i][j]/sqrt(N[i]*N[j])
print("---3.构造的相似矩阵---")
print(W)
return W
#3.根据用户的历史记录,给用户推荐物品
def recommandList(data,W,user,k=3,N=10):
rank={}
for i,score in data[user].items(): #获得用户user历史记录,如A用户的历史记录为{'a': '1', 'b': '1', 'd': '1'}
for j,w in sorted(W[i].items(),key=operator.itemgetter(1),reverse=True)[0:k]: #获得与物品i相似的k个物品
if j not in data[user].keys(): #该相似的物品不在用户user的记录里
rank.setdefault(j,0)
rank[j]+=float(score) * w
print("---4.推荐----")
print(sorted(rank.items(),key=operator.itemgetter(1),reverse=True)[0:N])
return sorted(rank.items(),key=operator.itemgetter(1),reverse=True)[0:N]
if __name__=='__main__':
# 用户,兴趣度,物品
# 实例1
uid_score_bid = ['A,1,a', 'A,1,b', 'A,1,d', 'B,1,b', 'B,1,c', 'B,1,e', 'C,1,c', 'C,1,d', 'D,1,b', 'D,1,c', 'D,1,d',
'E,1,a', 'E,1,d']
data=loadData(uid_score_bid) #获得数据
W=similarity(data) #计算物品相似矩阵
recommandList(data,W,'A',3,10) #推荐
# 实例2
users2 = []
fp_2 = open("u.data", "r", encoding='utf-8')
for line2 in fp_2.readlines():
line_2=line2.replace("\t",",")
lines2 = line_2.strip().split("\n")
users2+=lines2
data2 = loadData2(users2) # 获得数据
W2 = similarity(data2) # 计算物品相似矩阵
recommandList(data2, W3, '160', 3, 20) # 推荐
u.data为:电影数据集中的MovieLens 100K Dataset数据集,格式如下:
四列数据分别表示:用户,电影,用户对该电影的评分,时间戳
运行结果:
(1)实例1:
----1.用户:物品的倒排----
{'A': {'a': '1', 'b': '1', 'd': '1'}, 'B': {'e': '1', 'c': '1', 'b': '1'}, 'C': {'c': '1', 'd': '1'}, 'D': {'c': '1', 'b': '1', 'd': '1'}, 'E': {'a': '1', 'd': '1'}}
---2.构造的共现矩阵---
N: {'e': 1, 'a': 2, 'c': 3, 'b': 3, 'd': 4}
C: {'e': {'c': 1, 'b': 1}, 'a': {'b': 1, 'd': 2}, 'c': {'e': 1, 'b': 2, 'd': 2}, 'b': {'e': 1, 'a': 1, 'd': 2, 'c': 2}, 'd': {'a': 2, 'b': 2, 'c': 2}}
---3.构造的相似矩阵---
{'e': {'c': 0.5773502691896258, 'b': 0.5773502691896258}, 'a': {'b': 0.4082482904638631, 'd': 0.7071067811865475}, 'b': {'e': 0.5773502691896258, 'a': 0.4082482904638631, 'd': 0.5773502691896258, 'c': 0.6666666666666666}, 'd': {'a': 0.7071067811865475, 'b': 0.5773502691896258, 'c': 0.5773502691896258}, 'c': {'e': 0.5773502691896258, 'b': 0.6666666666666666, 'd': 0.5773502691896258}}
---4.推荐----
[('c', 1.2440169358562925), ('e', 0.5773502691896258)]
(3)实例2:
---4.推荐----
[('181', 20.64840599973269), ('121', 20.2794839377142), ('204', 18.271478272211656), ('96', 11.023442426199532), ('98', 10.777641929892148), ('172', 10.180678637454639), ('298', 9.773285712469288), ('210', 9.596102361983759), ('257', 9.471857683480076), ('134', 8.32171892672107), ('258', 7.5806418633821995), ('219', 7.417880077871427), ('170', 6.115343821235447), ('60', 5.947767646785022), ('411', 5.656864731597982), ('435', 5.330592647085664), ('479', 5.300291573010105), ('89', 5.273465875050527), ('189', 5.184142562541977), ('197', 5.114835789436526)]