能够快速实现的协同推荐

对于中小型的公司,用户的数据量及公司产品的个数都是较小规模的,需要提供给用户的推荐系统实现的重心也从人性化变成了实现成本,协同推荐就是非常常见、有效且可以快速实现的方法,也是本文想介绍的。

常规的快速简单推荐系统实现方法不排除以下几种:

  • 热门推荐
    所有人打开浏览的内容都一致,惊喜性会有所缺失,但是实现特别简单,稍加逻辑带给用户的体验感满足了基本需求。

  • SVD+推荐
    之前也讨论过实现方法了,附上链接:SVD及扩展的矩阵分解方法

  • 基于模型推荐
    这个比较偏向业务场景,可以说是经典的场景化模型,之前写过一篇基于用户特征的偏好推荐,可以参考一下:苏宁易购的用户交叉推荐

  • 协同推荐
    这个也是几乎每个公司都会用的,也是非常非常常见有效的算法之一


协同推荐介绍

首先,我们先来了解一下什么叫做协同推荐。
基于用户的协同过滤推荐算法是最早诞生的,1992年提出并用于邮件过滤系统,两年后1994年被 GroupLens 用于新闻过滤。一直到2000年左右,该算法都是推荐系统领域最著名的算法。算是非常古董级别的算法之一了,但是古董归古董,它的效果以及实现的成本却奠定了它在每个公司不可取代的地位。

基于用户的协同推荐

1
2
3
用户u1喜欢的电影是A,B,C
用户u2喜欢的电影是A, C, E, F
用户u3喜欢的电影是B,D

假设u1、u2、u3用户喜欢的电影分布如上,基于用户的协同推荐干了这么一件事情,它根据每个用户看的电影(A、B、C、…)相似程度,来计算用户之间的相似程度,将高相似的用户看过但是目标用户还没有看过的电影推荐给目标用户。

基于商品的协同推荐

1
2
3
4
5
6
电影A被u1,u2看过
电影B被u1,u3看过
电影C被u1,u2看过
电影D被u3看过
电影E被u2看过
电影F被u2看过

假设A~F电影被用户观影的分布如上,基于商品的协同推荐干了这么一件事情,它根据电影(A、B、C、…)被不同用户观看相似程度,来计算电影之间的相似程度,根据目标用户看过的电影的高相似度的电影推荐给目标用户。

看起来以上的逻辑是非常简单的,其实本来也是非常简单的,我看了下,网上关于以上的代码实现还是比较林散和有问题的,优化了python版本的code,并详细解释了每一步,希望,对初学者有所帮助。

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
#time  2017-09-17
#author:shataowei
#based-item

#所要的基础包比较简单
from collections import defaultdict
import math
import time
startTime = time.time()

#读取数据的过程
#/Users/slade/Desktop/machine learning/data/recommender/u1.base
def readdata(location):
list2item = {} #商品对应的用户列表(1:[[1,2],[2,3]]代表商品1对应用户1的行为程度为2,商品1对应的用户2的行为程度为3)
list2user = {} #用户对应的商品列表(1:[[1,2],[2,3]]代表用户1对应商品1的行为程度为2,用户1对应的商品2的行为程度为3)
f = open(location,'r')
data = f.readlines()
data = [x.split('\t') for x in data]
f.close()

for i in data:
if int(i[1]) not in list2item.keys():
list2item[int(i[1])] = [[int(i[0]),int(i[2])]]
else:
list2item[int(i[1])].append([int(i[0]),int(i[2])])

if int(i[0]) not in list2user.keys():
list2user[int(i[0])] = [[int(i[1]),int(i[2])]]
else:
list2user[int(i[0])].append([int(i[1]),int(i[2])])
return list2item,list2user
#list2item,list2user=readdata('/Users/slade/Desktop/machine learning/data/recommender/u1.base')

## 基于item的协同推荐
#0.将用户行为程度离散化:浏览:1,搜索:2,收藏:3,加车:4,下单未支付5
#1.计算item之间的相似度:item共同观看次数/单item次数连乘
#2.寻找目标用户观看过的item相关的其他item列表
#3.计算其他item的得分:相似度*用户行为程度,求和

#0 hive操作

# 1.1统计各商品出现次数
def itemcf_itemall(userlist = list2user):
I={}
for key in userlist:
for item in userlist[key]:
if item[0] not in I.keys():
I[item[0]] = 0
I[item[0]] = I[item[0]] + 1
return I

# 1.2计算相似矩阵
def itemcf_matrix(userlist = list2user):
C=defaultdict(defaultdict)
W=defaultdict(defaultdict)
#根据用户的已购商品来形成对应相似度矩阵
for key in userlist:
for item1 in userlist[key]:
for item2 in userlist[key]:
if item1[0] == item2[0]:
continue
if item2 not in C[item1[0]].keys():
C[item1[0]][item2[0]] = 0
C[item1[0]][item2[0]] = C[item1[0]][item2[0]] + 1
#计算相似度,并填充上面对应的相似度矩阵
for i , j in C.items():
for z , k in j.items():
W[i][z] = k/math.sqrt(I[i]*I[z])
#k/math.sqrt(I[i]*I[z])计算相似度,其中k为不同商品交集,sqrt(I[i]*I[z])用来压缩那些热门商品必然有高交集的问题
return W

#2.寻找用户观看的其他item
def recommendation(userid,k):
score_final = defaultdict(int)
useriditem = []
for item,score in list2user[userid]:
#3.计算用户的item得分,k来控制用多少个相似商品来计算最后的推荐商品
for i , smimilarity in sorted(W[item].items() , key = lambda x:x[1] ,reverse =True)[0:k]:
for j in list2user[userid]:
useriditem.append(j[0])
if i not in useriditem:
score_final[i] = score_final[i] + smimilarity * score
#累加每一个商品用户的评分与其它商品的相似度积的和作为衡量
#最后的10控制输出多少个推荐商品
l = sorted(score_final.items() , key = lambda x : x[1] , reverse = True)[0:10]
return l

#I = itemcf_itemall()
#W = itemcf_matrix()
#result_userid = recommendation(2,k=20)

endTime = time.time()
print endTime-startTime

python来实现基于item的协同推荐就完成了,核心的相似度计算可以根据实际问题进行修改,整体流程同上即可,当然数据量大的时候分布式去写也是可以的。


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
#time  2017-09-17
#author:shataowei
#based-user

from collections import defaultdict
import math
import time
startTime = time.time()

#读取数据
#/Users/slade/Desktop/machine learning/data/recommender/u1.base
def readdata(location):
list2item = {} #商品对应的用户列表
list2user = {} #用户对应的商品列表
f = open(location,'r')
data = f.readlines()
data = [x.split('\t') for x in data]
f.close()

for i in data:
if int(i[1]) not in list2item.keys():
list2item[int(i[1])] = [[int(i[0]),int(i[2])]]
else:
list2item[int(i[1])].append([int(i[0]),int(i[2])])

if int(i[0]) not in list2user.keys():
list2user[int(i[0])] = [[int(i[1]),int(i[2])]]
else:
list2user[int(i[0])].append([int(i[1]),int(i[2])])
return list2item,list2user
#list2item,list2user=readdata('/Users/slade/Desktop/machine learning/data/recommender/u1.base')

#基于用户的协同推荐
#0.先通过hive求出近一段时间(根据业务频率定义),用户商品的对应表
#1.求出目标用户的邻居,并计算目标用户与邻居之间的相似度
#2.列出邻居所以购买的商品列表
#3.针对第二步求出了商品列表,累加所对应的用户相似度,并排序求top

#0.hive操作

#1.1求出目标用户的邻居,及对应的相关程度
def neighbour(userid,user_group = list2user,item_group = list2item):
neighbours = {}
for item in list2user[userid]:
for user in list2item[item[0]]:
if user[0] not in neighbours.keys():
neighbours[user[0]] = 0
neighbours[user[0]] = neighbours[user[0]] + 1
return neighbors
#通常来说,基于item的推荐对于商品量较大的业务会构成一个巨大的商品矩阵,这时候如果用户人均购买量较低的时候,可以考虑使用基于user的推荐,它在每次计算的时候会只考虑相关用户,也就是这边的neighbours(有点支持向量基的意思),大大的降低了计算量。
#neighbours = neighbour(userid=2)

#1.2就算用户直接的相似程度,这边用的余弦相似度:点积/模的连乘
def similarity(user1,user2):
x=0
y=0
z=0
for item1 in list2user[user1]:
for item2 in list2user[user2]:
if item1[0]==item2[0]:
x1 = item1[1]*item1[1]
y1 = item2[1]*item2[1]
z1 = item1[1]*item2[1]
x = x + x1
y = y + y1
z = z + z1
#避免分母为0
if x * y == 0 :
simi = 0
else:
simi = z / math.sqrt(x * y)
return simi

#1.3计算目标用户与邻居之间的相似度:
def N_neighbour(userid,neighbours,k):
neighbour = neighbours.keys()
M = []
for user in neighbour:
simi = similarity(userid,user)
M.append((user,simi))
M = sorted(M,key = lambda x:x[1] ,reverse = True)[0:k]
return M

#M = N_neighbour(userid,neighbours,k=200)

#2.列出邻居所购买过的商品并计算商品对应的推荐指数
def neighbour_item(M=M):
R = {}
M1 = dict(M)
for neighbour in M1:
for item in list2user[neighbour]:
if item[0] not in R.keys():
R[item[0]] = M1[neighbour] * item[1]
else:
R[item[0]] = R[item[0]] + M1[neighbour] * item[1]
#根据邻居买过什么及与邻居的相似度,计算邻居买过商品的推荐度
return R
# R = neighbour_item(M)

#3.排序得到推荐商品
Rank = sorted(R.items(),key=lambda x:x[1],reverse = True)[0:50]

endTime = time.time()
print endTime-startTime

python来实现基于user的协同推荐就完成了,核心的相似度计算可以根据实际问题进行修改,基于user的实现过程中,用了邻居这个概念,大大降低了计算量,我用了大概20万用户,2千的商品数,基于user的推荐实现速度大概为基于商品的10分之一,效果差异却相差不大。

协同推荐是非常简单的推荐入门算法之一,也是必须要手动快速代码实现的算法之一,希望能给大家一些帮助。

打赏的大佬可以联系我,赠送超赞的算法资料