给用户推荐那些和他们之前喜欢的物品相似的物品。item-based CF并不是利用物品的内容属性计算物品之间的相似度,主要是通过分析用户的行为记录计算物品间的相似度。
(1)计算物品之间的相似度
(2)根据物品的相似度和用户的历史行为,给用户生成推荐列表
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)∣
其中 ∣ 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。但如果物品 j j j很热门,即很多人都喜欢,则 w i j w_{ij} wij就会很大,接近1;即任何物品都会和热门的物品有很大的相似度,所以添加惩罚物品 j j j的权重
w i j = ∣ N ( i ) ∩ N ( j ) ∣ ∣ N ( i ) ∣ ∣ N ( j ) ∣ w_{ij}=\frac{|N(i)\cap N(j)|}{\sqrt{|N(i)||N(j)|}} wij=∣N(i)∣∣N(j)∣∣N(i)∩N(j)∣
对每个用户建立一个包含他”喜欢的物品的列表“,然后对每个用户,将他”喜欢的物品的列表“中的物品两两在共现矩阵 C C C中加1。
对每个用户”喜欢的物品的列表“建立”物品两两对应出现矩阵“,最终将每个用户的”物品两两对应出现矩阵“相加得到共现矩阵 C C C, C [ i ] [ j ] C[i][j] C[i][j]表示同时喜欢物品 i i i和物品 j j j的用户数。
import math
def ItemSimilarity(train):
"""
:param train: user-item matrix
:return: W: similarity matrix
"""
#calculate co-rated users between items
C=dict()
N=dict()
for u, items in train.items():
for i in items:
#################
if i not in N:
N[i] = 0
#################
N[i] += 1
for j in items:
if i == j:
continue
#################
if i not in C:
C[i] = dict()
if j not in C[i]:
C[i][j]=0
#################
C[i][j] += 1
#calculate final similarity matrix W
W=dict()
for i, related_items in C.items():
for j, cij in related_items.items():
#################
if i not in W:
W[i] = dict()
if j not in W[i]:
W[i][j] = 0
#################
W[i][j] = cij/math.sqrt(N[i]*N[j])
return W
#test:
train={'A':{'a','b','d'},
'B':{'b','c','e'},
'C':{'c','d'},
'D':{'b','c','d'},
'E':{'a','d'}}
print(ItemSimilarity(train))
#Output:
{'a': {'b': 0.4082482904638631, 'd': 0.7071067811865475},
'b': {'a': 0.4082482904638631, 'd': 0.5773502691896258, 'c': 0.6666666666666666, 'e': 0.5773502691896258},
'd': {'a': 0.7071067811865475, 'b': 0.5773502691896258, 'c': 0.5773502691896258},
'c': {'b': 0.6666666666666666, 'e': 0.5773502691896258, 'd': 0.5773502691896258},
'e': {'b': 0.5773502691896258, 'c': 0.5773502691896258}}
p u j = ∑ i ∈ N ( u ) ∩ S ( j , K ) w j i r u i p_{uj}=\sum\limits_{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)是用户 u u 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的兴趣。即,和”用户历史上感兴趣的物品“越相似的物品,在用户的推荐列表里越有可能排名靠前。
from operator import itemgetter
def Recommendation(train, user_id, W, K):
"""
:param train: user-item matrix
:param user_id: user id
:param W: similarity matrix
:param K: number of similar items
:return: recommendation list in order
"""
#give final recommendation list
rank=dict()
ru=train[user_id] #user_id喜欢的物品集
for i in ru: #user_id喜欢的物品i
if i not in W:
continue
for j, wj in sorted(W[i].items(), key=itemgetter(1), reverse=True)[0:K]: #与物品i相似度最大的K个物品
if j in ru: #如果与物品i 相似度在前K个位置的物品j 在user_id原本喜欢的物品集里
continue
#################
if j not in rank:
rank[j] = 0
#################
rank[j] += wj
return rank
#test:
train={'A':{'a','b','d'},
'B':{'b','c','e'},
'C':{'c','d'},
'D':{'b','c','d'},
'E':{'a','d'}}
W=ItemSimilarity(train)
print(Recommendation(train, 'A', W, 1))
print(Recommendation(train, 'A', W, 3))
#Output:
{'c': 0.6666666666666666}
{'c': 1.2440169358562925, 'e': 0.5773502691896258}
#即遍历指定用户喜欢的每个物品的/前K个相似物品