Artificial Intelligence
协同过滤算法
协同过滤概念
协同过滤(Collaborative Filtering,简称CF)简单来说是利用某兴趣相投、拥有共同经验的群体的喜好来推荐目标用户感兴趣的信息,个人通过合作的机制给予信息相当程度的回应(如评分)并记录下来以达到过滤的目的进而帮助别人筛选信息,回应不一定局限于特别感兴趣的,特别不感兴趣信息的纪录也相当重要。
余弦定理相似性度量
余弦距离通过向量空间中两个向量夹角的余弦值作为衡量两个个体间差异的大小的度量。
三角形余弦定理公式:
c
o
s
A
=
b
2
+
c
2
−
a
2
2
b
c
cosA=\frac{b^{2}+c^{2}-a^{2}}{2bc}
cosA=2bcb2+c2−a2
由三角形余弦定理公式可知,角A越小,bc两边越接近。当A为0度时,bc两边完全重合。
在向量空间中,对于向量a和向量b符合公式:
c
o
s
C
=
<
a
⃗
,
b
⃗
>
∣
a
⃗
∣
∣
b
⃗
∣
cosC=\frac{<\vec a,\vec b>}{|\vec a||\vec b|}
cosC=∣a∣∣b∣<a,b>
当a和b越接近(越相似)时,余弦值就越大,最大为1.
所以在比对a和b的相似度时,可以将其向量化后,计算它的余弦值,从而比较其相似度。即:
s
i
m
(
a
,
b
)
=
c
o
s
(
a
⃗
b
⃗
)
=
a
⃗
⋅
b
⃗
∣
a
⃗
∣
⋅
∣
b
⃗
∣
sim(a,b)=cos(\vec a\vec b)=\frac{\vec a·\vec b}{|\vec a|·|\vec b|}
sim(a,b)=cos(ab)=∣a∣⋅∣b∣a⋅b
类似的也可以推广到多个样本的相似性度量公式:
s
i
m
(
a
,
b
,
⋅
⋅
⋅
)
=
c
o
s
θ
=
x
1
y
1
+
x
2
y
2
+
⋅
⋅
⋅
+
x
n
y
n
x
1
2
+
x
2
2
+
⋅
⋅
⋅
+
x
n
2
⋅
y
1
2
+
y
2
2
+
⋅
⋅
⋅
+
y
n
2
sim(a,b,···)=cosθ=\frac{x_{1}y_{1}+x_{2}y_{2}+···+x_{n}y_{n}}{\sqrt{x_{1}^{2}+x_{2}^{2}+···+x_{n}^{2}}·\sqrt{y_{1}^{2}+y_{2}^{2}+···+y_{n}^{2}}}
sim(a,b,⋅⋅⋅)=cosθ=x12+x22+⋅⋅⋅+xn2⋅y12+y22+⋅⋅⋅+yn2x1y1+x2y2+⋅⋅⋅+xnyn
对于集合A和集合B的相似性度量,为了方便计算,我们往往会用到下面的公式(计算的结果大小和上面的公式计算的结果是一样的):
s
i
m
(
A
,
B
)
=
A
∩
B
∣
A
∣
⋅
∣
B
∣
sim(A,B)=\frac{A∩B}{\sqrt{|A|·|B|}}
sim(A,B)=∣A∣⋅∣B∣A∩B
基于用户的协同过滤
算法介绍
基于用户的 CF 的基本思想相当简单,基于用户对物品的偏好找到相邻邻居用户,然后将邻居用户喜欢的推荐给当前用户。计算上,就是将一个用户对所有物品的偏好作为一个向量来计算用户之间的相似度,找到 K 邻居后,根据邻居的相似度权重以及他们对物品的偏好,预测当前用户没有偏好的未涉及物品,计算得到一个排序的物品列表作为推荐。 下图给出了一个例子,对于用户 A,根据用户的历史偏好,这里只计算得到一个邻居 - 用户 C,然后将用户 C 喜欢的物品 D 推荐给用户 A。
1)(建立物品-用户的倒查表)
2)建立用户-用户的稀疏矩阵(共现矩阵/同现矩阵)
3)建立相似矩阵(相似度计算:找到和目标用户兴趣相似的用户集合)
4)推荐(构建推荐矩阵:找到这个集合中用户喜欢的,且目标用户没有听说过的物品推荐给目标用户)
1.根据相似度的高低找到各用户的相似用户;
用户A的相似用户为用户C;
用户B的相似用户为用户C;
用户C的相似用户为用户B。
2.找到相似用户购买过而目标用户不知道的物品,计算目标用户对这样的物品感兴趣的预测值(就是预测目标用户购买的可能性),向目标用户推荐这些物品。
计算公式为:
p
(
u
,
i
)
=
∑
v
∈
S
(
u
,
k
)
∩
N
(
i
)
w
u
v
r
v
i
p(u,i)=\sum_{v∈S(u,k)∩N(i)}w_{uv}r_{vi}
p(u,i)=v∈S(u,k)∩N(i)∑wuvrvi
其中,S(u, K)包含和目标用户u兴趣最接近的K个用户,N(i)是购买过物品i的用户集合,wuv是目标用户u和用户v的兴趣相似度,rvi代表用户v对物品i的兴趣,因为使用的是单一行为的隐反馈数据,所以所有的rvi=1。
例如:我们默认K=1, p ( A , b ) = 1 2 , p ( B , c ) = 3 2 p(A,b)=\frac{1}{\sqrt{2}}, p(B,c)=\frac{\sqrt{3}}{2} p(A,b)=21,p(B,c)=23
3.给用户生成推荐列表,将被推荐物品展示给相应的用户。
代码实现
import numpy as np
from math import sqrt
'''
用户/物品 | 物品a | 物品b | 物品c | 物品d
用户A | √ | | √ |
用户B | √ | √ | | √
用户C | √ | √ | √ | √
'''
#定义余弦相似性度量计算
def cosine(ls_1,ls_2,m):
Numerator = 0 #公式中的分子
abs_1 = abs_2 = 0 #分母中两向量的绝对值
for i in range(m):
Numerator += ls_1[i] * ls_2[i]
if ls_1[i] == 1:
abs_1 += ls_1[i]
if ls_2[i] == 1:
abs_2 += ls_2[i]
Denominator = sqrt(abs_1 * abs_2) #公式中的分母
return Numerator/Denominator
#定义预测函数
def predict(w_uv,r_vi=1):
p = w_uv * r_vi
return p
if __name__ == "__main__":
#建立用户-物品矩阵
user_item = np.array([[1,0,1,0],
[1,1,0,1],
[1,1,1,1]])
print("用户-物品矩阵:")
print(user_item)
user = ['用户A','用户B','用户C']
item = ['物品a','物品b','物品c','物品d']
n = len(user) #n个用户
m = len(item) #m个物品
K = 1 #只找到一个最相似用户
#构建用户-用户相似度矩阵
sim = np.zeros((n,n)) #相似度矩阵,默认全为0
for i in range(n):
for j in range(n):
if i < j:
sim[i][j] = cosine(user_item[i],user_item[j],m)
sim[j][i] = sim[i][j]
print("得到的用户-用户相似度矩阵:")
print(sim) #打印用户-用户相似度矩阵
#推荐物品
max_sim = [0,0,0] #存放每个用户的相似用户
r_list = [[],[],[]] #存放推荐给每个用户的物品
p = [[],[],[]] #每个用户被推荐物品的预测值列表
for i in range(n): #n个用户循环n次
#找到与用户i最相似的用户
for j in range(len(sim[i])): #range()里面写n也可以
if max(sim[i]) != 0 and sim[i][j] == max(sim[i]):
max_sim[i] = user[j] #此时的j就是相似用户的编号
break #break目的:一是结束当前循环,二是当前的j后面有用
if max_sim[i] == 0:
continue #等于0,表明当前用户无相似用户,无需推荐,继续下个用户
#找出应该推荐的物品,并计算预测值
for k in range(K): #为了更契合预测值计算公式,因为这里K=1,所以也可以省去这个for
for x in range(m): #m个物品循环m次
if user_item[i][x] == 0 and user_item[j][x] == 1: #目标用户不知道,而相似用户知道
r_list[i].append(item[x])
p[i].append(predict(sim[i][j]))
#打印结果
for i in range(n): #n个用户循环n次
if len(r_list[i]) > 0: #当前用户有被推荐的物品
print("向{:}推荐的物品有:".format(user[i]),end='')
print(r_list[i])
print("该用户对以上物品该兴趣的预测值为:",end='')
print(p[i])
print()
运行结果
基于物品的协同过滤
算法介绍
基于物品的 CF 的原理和基于用户的 CF 类似,只是在计算邻居时采用物品本身,而不是从用户的角度,即基于用户对物品的偏好找到相似的物品,然后根据用户的历史偏好,推荐相似的物品给他。从计算的角度看,就是将所有用户对某个物品的偏好作为一个向量来计算物品之间的相似度,得到物品的相似物品后,根据用户历史的偏好预测当前用户还没有表示偏好的物品,计算得到一个排序的物品列表作为推荐。下图给出了一个例子,对于物品 A,根据所有用户的历史偏好,喜欢物品 A 的用户都喜欢物品 C,得出物品 A 和物品 C 比较相似,而用户 C 喜欢物品 A,那么可以推断出用户 C 可能也喜欢物品 C。
-
物品a的相似物品为物品c;
物品b的相似物品为物品a;
物品c的相似物品为物品a。 -
根据物品的相似度和用户的历史行为给用户生成推荐列表
通过如下公式计算用户u对一个物品j的购买预测值:
p ( u , j ) = ∑ i ∈ N ( u ) ∩ S ( i , k ) w j i r u i p(u,j)=\sum_{i∈N(u)∩S(i,k)}w_{ji}r_{ui} p(u,j)=i∈N(u)∩S(i,k)∑wjirui其中,p(u,j)表示用户u对物品j的兴趣,N(u)表示用户喜欢的物品集合(i是该用户喜欢的某一个物品),S(i,k)表示和物品i最相似的K个物品集合(j是这个集合中的某一个物品), w j i w_{ji} wji表示物品j和物品i的相似度
, r u i r_{ui} rui表示用户u对物品i的兴趣(这里简化 r u i r_{ui} rui都等于1)。
如: p ( C , c ) = 2 6 p(C,c)=\frac{2}{\sqrt{6}} p(C,c)=62
-
对于物品a,它的相似物品为物品c,购买过物品a的用户中,用户C没有购买过物品c,所以我们要向用户C推荐物品c,用户C对物品c感兴趣的预测值为 p ( C , c ) = 2 6 p(C,c)=\frac{2}{\sqrt{6}} p(C,c)=62;
对于物品b,它的相似物品为物品a,但是购买过物品b的用户都购买过物品a;
对于物品c,它的相似物品为物品a,但是购买过物品c的用户也都购买过物品a。
代码实现
import numpy as np
from math import sqrt
'''
用户/物品 | 物品a | 物品b | 物品c
用户A | √ | | √
用户B | √ | | √
用户C | √ | √ |
'''
#定义余弦相似性度量计算
def cosine(ls_1,ls_2,m):
Numerator = 0 #公式中的分子
abs_1 = abs_2 = 0 #分母中两向量的绝对值
for i in range(m):
Numerator += ls_1[i] * ls_2[i]
if ls_1[i] == 1:
abs_1 += ls_1[i]
if ls_2[i] == 1:
abs_2 += ls_2[i]
Denominator = sqrt(abs_1 * abs_2) #公式中的分母
return Numerator/Denominator
#定义预测函数
def predict(w_uv,r_vi=1):
p = w_uv * r_vi
return p
if __name__ == "__main__":
#建立用户-物品矩阵
user_item = np.array([[1,0,1],
[1,0,1],
[1,1,0]])
print("用户-物品矩阵:")
print(user_item)
user = ['用户A','用户B','用户C']
item = ['物品a','物品b','物品c']
n = len(user) #n个用户
m = len(item) #m个物品
K = 1 #只找到一个最相似物品
#建立物品-用户倒排表
item_user = user_item.T
print("物品-用户矩阵:")
print(item_user)
#构建物品-物品相似度矩阵
sim = np.zeros((n,n)) #相似度矩阵,默认全为0
for i in range(n):
for j in range(n):
if i < j:
sim[i][j] = cosine(item_user[i],item_user[j],m)
sim[j][i] = sim[i][j]
print("得到的物品-物品相似度矩阵:")
print(sim) #打印物品-物品相似度矩阵
#推荐物品
max_sim = [0,0,0] #存放每个物品的相似物品
r_list = [[],[],[]] #存放推荐给每个用户的物品
p = [[],[],[]] #每个用户被推荐物品的预测值列表
for i in range(m): #m个物品循环m次
#找到与物品i最相似的物品
for j in range(len(sim[i])): #range()里面写m也可以
if max(sim[i]) != 0 and sim[i][j] == max(sim[i]):
max_sim[i] = item[j] #此时的j就是相似物品的编号
break #break目的:一是结束当前循环,二是当前的j后面有用
if max_sim[i] == 0:
continue #等于0,表明当前物品无相似物品,继续下个物品
#找出应该推荐的物品,并计算预测值
for k in range(K): #为了更契合预测值计算公式,因为这里K=1,所以也可以省去这个for
for x in range(n): #n个用户循环n次
if item_user[i][x] == 1 and item_user[j][x] == 0:#当前物品用户知道,而相似物品该用户不知道
r_list[x].append(max_sim[i])
p[x].append(predict(sim[i][j]))
#打印结果
for i in range(n): #n个用户循环n次
if len(r_list[i]) > 0: #当前用户有被推荐的物品
print("向{:}推荐的物品有:".format(user[i]),end='')
print(r_list[i])
print("该用户对以上物品该兴趣的预测值为:",end='')
print(p[i])
print()