推荐算法之物品协同过滤

一、概念

用户协同过滤并不总是合适的。

用户协同过滤的缺陷
(1)拓展性:用户协同过滤的计算量会随着用户数目的增长而增长。在几千用户时效果还好,但是有上百万用户时拓展性就成了一个问题。
(2)稀疏性:大部分推荐系统中,用户和商品都很多,但是用户评级的平均商品数目较少。所以使用用户协同过滤有可能找不到最近邻居。

为了解决用户协同过滤的缺陷,于是又有了物品协同过滤(ItemCF)。ItemCF 的原理是,为用户推荐那些和他之前喜欢的物品相似的物品。例如,算法可能会因为用户买过《推荐系统实践》而为用户推荐《推荐系统》。
可以抽象为user->item->item, 推荐与其喜欢的item相似的item,简称item-based

对比user-based和item-based
user-based更多的考虑相同爱好的用户兴趣,推荐这些用户喜欢/访问过的item,和用户当前的行为关系不大,更多的是用户的这些朋友访问过什么,属于圈子的社会化行为,推荐的item是相同爱好用户最喜欢的item,所以具备热点效应,也就是推荐圈子用户访问最多的;同时也可以将圈子用户刚刚访问item推荐出来,具备很强的实时性,尤其是新引入的热点,可以很快的扩散,也能解决new-item的冷启动问题。
item-based 主要考虑用户历史兴趣,推荐与用户历史喜欢item相似的item,和用户的当前行为有很大的关系,推荐的item与用户当前click的相似性,用户是可以理解的,也就是所谓的可解释性很强,推荐的item也不是热门的,很有可能是冷门(长尾),但是和用户的兴趣相关,要求用户在这个网站上的兴趣是长久和固定的,推荐的意义在于帮助用户找到和其兴趣相关的item。

二、实现物品协同过滤

1、执行步骤

step 1:建立物品-物品共现矩阵
step 2:计算物品间相似度
step 3:针对目标用户u,对其浏览过的物品找到其最相似的K个物品,产生N个推荐

2、代码实现

采用GroupLens提供的MovieLens数据集 ,包含6000多用户对4000多部电影的100万条评分。该数据集是一个评分数据集,用户可以给电影评1-5分5个不同的等级。
本文着重研究隐反馈数据集中TopN推荐问题,因此忽略了数据集中的评分记录。也就是说,TopN推荐的任务是预测用户会不会对某电影评分,而不是预测用户在准备对某部电影评分的前提下会给电影评多少分。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# coding=utf-8
'''
物品协同过滤推荐
'''
import random
import math
from operator import itemgetter

#========================================================
# 核心算法
#========================================================

class itemBasedCF():

def __init__(self):
'''
初始化相关参数
'''
# 每个物品查找相似个数
self.n_sim_item = 10
# 推荐数量
self.n_rec_item = 10

# 训练用的数据集
self.dataSet = {}

# 物品相似度矩阵
self.item_sim_matrix = {} # 使用典中典模拟矩阵

print('配置:推荐数 = %d' % self.n_rec_item)

def load_file(self, filename):
'''
读文件,返回文件的每一行
INPUT -> 文件名
'''
with open(filename, 'r') as f:
for i, line in enumerate(f):
if i == 0: # 去掉文件第一行的title
continue
yield line.strip('\r\n')
print('Load %s success!' % filename)

def get_dataset(self, filename):
'''
读文件得到评分数据
INPUT -> 文件名
'''
dataSet_len = 0
for line in self.load_file(filename):
user, item, rating, timestamp = line.split('::')
self.dataSet.setdefault(user, {})
# 键中键:形如{'1': {'1287': '2.0', '1953': '4.0', '2105': '4.0'}, '2': {'10': '4.0', '62': '3.0'}}
# 用户1看了id为1287的电影,打分2.0
self.dataSet[user][item] = rating
dataSet_len += 1
print('样本量 = %s' % dataSet_len)

def itemSimilarity(self):
'''
计算物品间相似度
'''
print("STEP1:构建item-item共现矩阵...")
# item-item共现矩阵C_matrix
C_matrix = dict()
# 物品被多少个不同用户购买
item_hot = dict()
for user, items in self.dataSet.items():
for i in items.keys():
item_hot.setdefault(i, 0)
item_hot[i] += 1
C_matrix.setdefault(i, {})
for j in items.keys():
if i == j:
continue
C_matrix[i].setdefault(j, 0)
weight = 1
# 根据热门程度加权
# weight = 1 / math.log2(1+len(items))
C_matrix[i][j] += weight
print("item-item共现矩阵构建完成! ")

print('STEP2:计算物品间相似度 ...')
for i, related_items in C_matrix.items():
for j, count in related_items.items(): # count表示物品与物品共现次数
# 计算物品间相似度
self.item_sim_matrix[i][j] = count / math.sqrt(item_hot[i] * item_hot[j])
print('物品间相似度计算完成!')

def recommend(self, user):
'''
针对目标用户,产生N个推荐
INPUT -> 待推荐用户
'''
K = self.n_sim_item
N = self.n_rec_item
rank = {}
watched_items = self.dataSet[user]

for i, interest in watched_items.items():
for j, sim in sorted(self.item_sim_matrix[i].items, key=itemgetter(1), reverse=True)[0:K]:
if j in watched_items:
continue
rank.setdefault(j, 0)
rank[j] += float(interest) * float(sim)
return sorted(rank.items(), key=itemgetter(1), reverse=True)[0:N] # 返回最可能感兴趣的N个

#========================================================
# 主程序
#========================================================

if __name__ == '__main__':

rating_file = 'ratings3.csv'
# 初始化
itemCF = itemBasedCF()
# 读取评分文件
itemCF.get_dataset(rating_file)
# 构建物品相似度矩阵
itemCF.itemSimilarity()
0%