在上篇文章中简单实现了基于用户的协同过滤算法(userCF),该算法存在一些缺点:
基于物品的协同过滤算法(itemCF)的思想是给用户推荐 和用户喜欢的商品相似的商品,这就涉及到如何计算两个商品的相似度了。itemCF 中并不是利用物品本身的内容属性来计算相似度(这是基于内容的推荐),而是通过分析用户行为记录(评分、购买、点击、浏览等行为)来计算两个物品的相似度,同时喜欢物品A和物品B的用户数越多,就认为物品A和物品B越相似。下面介绍该算法的实现过程。
userId : 用户 ID
movieId : 用户看过的电影 ID
rating : 用户对所看电影的评分
timestap : 用户看电影的时间戳
N : 记录电影被多少用户看过,如: N[“1”] = 10 表示有10个用户看过ID 为 “1”的电影;
W : 相似矩阵,存储两个用户的相似度,如:W[“1”][“2”] = 0.66 表示ID 为 “1” 的电影和 ID 为 “2” 的电影相似度为 0.66 ;
train : 用户记录数据集中的数据, 格式为: train= { user : [[item1, rating1], [item2, rating2], …], …… }
k : 使用最相似的 k 个电影
n : 为用户推荐 n 部电影
具体计算如下(可以结合下面的源代码看):
首先遍历 train 变量 { user : [[item1, rating1], [item2, rating2], …],……} ,取出用户看过的电影列表,使用变量 i 遍历该用户看过的电影 ,对看过该电影的用户数(即变量 N(i) )加一,同时使用变量 j 遍历该用户看过的电影,如果 i 和 j 不同,则 W[i][j] 加一(此时W[i][j]记录的是同时看过电影 i 和电影 j 的用户数)。
遍历 W 矩阵,对 W 中的每一个元素进行如下计算:
W [ i ] [ j ] = W [ i ] [ j ] N ( i ) ∗ N ( j ) W[i][j] = \frac{W[i][j]}{\sqrt{N(i) * N(j)}} W[i][j]=N(i)∗N(j)W[i][j]
得到的 W 矩阵就是物品之间的相似度矩阵。
5. 推荐
首先获得用户已经看过的电影列表,遍历该电影列表,对于用户看过的电影 i ,找出与电影 i 最相似的前 k 个电影(对 W[i] 按照相似度排序),计算这 k 个电影各自的加权评分(rank):
r a n k j = ∑ i ( 用 户 对 电 影 i 的 评 分 ∗ 电 影 i 和 电 影 j 的 相 似 度 ) rank_{j}=\sum_i(用户对电影 i 的评分 * 电影 i 和电影 j 的相似度) rankj=i∑(用户对电影i的评分∗电影i和电影j的相似度)
对rank按照评分倒序排序,取前 n 个推荐给用户即可。
"""
@description: a demo for item_based collaborative filtering
@author: Chengcheng Zhao
@date: 2020-11-11 21:19:00
"""
import random
import operator
class ItemBasedCF:
def __init__(self):
self.N = {
} # number of item user have interacted
self.W = {
} # similarity matrix to store similarity of item i and item j
self.train = {
}
# recommend n items from the k most similar to the items user have interacted
self.k = 30
self.n = 10
def get_data(self, file_path):
"""
@description: load data from file
@param file_path: path of file
"""
print('start loading data from ', file_path)
with open(file_path, "r") as f:
for i, line in enumerate(f, 0):
if i != 0: # remove the first line that is title
line = line.strip('\r')
user, item, rating, timestamp = line.split(',')
self.train.setdefault(user, [])
self.train[user].append([item, rating])
print('loading data successfully')
def similarity(self):
"""
@description: caculate similarity between item i and item j
"""
print('start caculating similarity matrix ...')
for user, item_ratings in self.train.items():
items = [x[0] for x in item_ratings] # items that user have interacted
for i in items:
self.N.setdefault(i, 0)
self.N[i] += 1 # number of users who have interacted item i
for j in items:
if i != j:
self.W.setdefault(i, {
})
self.W[i].setdefault(j, 0)
self.W[i][j] += 1 # number of users who have interacted item i and item j
for i, j_cnt in self.W.items():
for j, cnt in j_cnt.items():
self.W[i][j] = self.W[i][j] / (self.N[i] * self.N[j]) ** 0.5 # similarity between item i and item j
print('caculating similarity matrix successfully')
def recommendation(self, user):
"""
@description: recommend n item for user
@param user: recommended user
@return items recommended for user
"""
print('start recommending items for user whose userId is ', user)
rank = {
}
watched_items = [x[0] for x in self.train[user]]
for i in watched_items:
for j, similarity in sorted(self.W[i].items(), key=operator.itemgetter(1), reverse=True)[0:self.k]:
if j not in watched_items:
rank.setdefault(j, 0.)
rank[j] += float(self.train[user][watched_items.index(i)][1]) * similarity # rating that user rate for item i * similarity between item i and item j
return sorted(rank.items(), key=operator.itemgetter(1), reverse=True)[0:self.n]
if __name__ == "__main__":
file_path = "C:\\Users\\DELL\\Desktop\\code\\python\\dataset\\ml-latest-small\\ratings.csv"
itemBasedCF = ItemBasedCF()
itemBasedCF.get_data(file_path)
itemBasedCF.similarity()
user = random.sample(list(itemBasedCF.train), 1)
rec = itemBasedCF.recommendation(user[0])
print('\nitems recommeded for user whose userId is', user[0], ':')
print(rec)