《推荐系统实践》 程序实现 —— 2.4.1 基于用户的协同过滤算法

本文为《推荐系统实践-项亮》中,关于2.4.1 基于用户的协同过滤算法的程序实现

  • 在查询本文中,关于原书某页的算法实现时,可以直接Crtl+F,按格式"PXX"输入原书页码(例:P45)查询。未添加的程序实现会在之后陆续补上。
  • 在概览程序思想时,可以将程序中所有的**print()**注释掉。在详细了解程序内容或练习的时候,建议将其中的解释性print()执行一遍 ,查看运行过程,通过中间过程,可以很好的帮助理解。
  • 程序中“”字的注释,表示此处与原书中不同,但是思想一致,只是在python语言的表述上不同。

源码下载:《推荐系统实践》 程序实现 —— 2.4.1 基于用户的协同过滤算法

1 基础算法

公式介绍:
书中选择了如下两种公式,每一种都可以用于计算两个用户的兴趣相似度。

P45Jaccard公式

在这里插入图片描述
用一个图片,介绍这个公式:
在这里插入图片描述
所以,O越大,两个人共同喜欢的物品越多,两个人的兴趣爱好越相似。

P45 余弦相似度公式

在这里插入图片描述

p45 实现余弦相似度,计算两两用户的相似度

import math
W = dict()
train = {'A':{'a','b','d'},'B':{'a','c'},'C':{'b','e'},'D':{'c','d','e'}} #{'a','b','c'}是一个集合
for u in train.keys():
	for v in train.keys():
		if u == v:
			continue
			
		W.update({u:{v:len(train[u]&train[v])}})#计算分子,'&'是 Python中集合的并运算
		W[u][v]/=math.sqrt(len(train[u])*len(train[v])*1.0)#分子/分母

for u,v_similarity in W.items():
	print(u,'corresponds to',v_similarity)	

执行结果

('A', 'corresponds to', {'D': 0.3333333333333333})
('C', 'corresponds to', {'D': 0.4082482904638631})
('B', 'corresponds to', {'D': 0.4082482904638631})
('D', 'corresponds to', {'B': 0.4082482904638631})

p46 建立物品-用户的倒排表(build inverse table for item_users)

train = dict();

train = {'A':{'a':1,'b':1,'d':1},
		 'B':{'a':1,'c':1},
		 'C':{'b':1,'e':1},
		 'D':{'c':1,'d':1,'e':1}
		}#此处物品a、b、c、d、e后面对应的值1,是用户对物品感兴趣的程度。比如'A':{'a':1},是用户A对物品a感兴趣的程度。
		 #按理说感兴趣的程度应该是不同的数值,但此处,因为使用的是单一行为的隐反馈数据,所以所有的都定为1了。这里的感兴趣程度即书中p47页公式(用于计算用户u对物品i的感兴趣程度)中的rvi,可参考其介绍。

#1、build inverse table for item_users:通过用户-物品列表,建立物品-用户倒排表
item_users = dict()#存储物品-用户倒排表
for u,items in train.items():
	#print(u,'corresponds to',items)
	for i in items.keys():#遍历每一个用户的物品列表。
		#print(i)
		if i not in item_users:
			item_users[i] = set()
		item_users[i].add(u)#物品列表中的物品i,有用户u访问过

print('输出item_users[]')
for u,items in item_users.items():
	print(u,'corresponds to',items)
print('')

执行结果

A corresponds to {'a': 1, 'b': 1, 'd': 1}
a
b
d
B corresponds to {'a': 1, 'c': 1}
a
c
C corresponds to {'b': 1, 'e': 1}
b
e
D corresponds to {'c': 1, 'd': 1, 'e': 1}
c
d
e
输出item_users[]
a corresponds to {'A', 'B'}
b corresponds to {'A', 'C'}
d corresponds to {'A', 'D'}
c corresponds to {'B', 'D'}
e corresponds to {'D', 'C'}

p46 建立用户相似度矩阵W(calculate co-rated items between users)

#2、calculate co-rated items between users :计算两个用户共同访问过的物品数,建立用户相似度矩阵C,C[u][v]=x,即表示u,v共同访问过的物品有x个。 
C = dict()#C[][]是一个嵌套的二维字典,eg:C = {'A':{'C':1,'B':1,'D':1}}
N = dict()#N[]统计用户有过行为的物品数,最终N[A]=3,N[B]=2,N[C]=2,N[D]=3
for i , users in item_users.items():
	print(i, 'corresponds to ', users) #示例:('a', 'corresponds to ', set(['A', 'B']))
	
	for u in users:#u遍历一遍物品i的users列表
		print(u ,'u in users')
		if u not in N.keys():
			N[u] = 0
		N[u] += 1 #统计用户u有过行为的物品数
        
		for v in users:#v遍历一遍物品i的users列表
			print(v ,'v in uers')
			if u == v:
				continue
			
			#注意:二维“字典”新添一个key-value对时,需要判断key是否已经存在了
			if u not in C.keys():
				C.update({u:{v:0}})
			
			if v not in C[u].keys():
				C[u].update({v:0})
				
			C[u][v] += 1 #用户u、v对同一个物品有过行为。经过u,v两层遍历,C[][]会成为一个对称矩阵。
			print(C)

print('\n输出N[]:')
for user,value in N.items():
	print(user,'corresponds to',value)
	
print('\n输出C[][]:')
for u,related_users in C.items():
	print(u,'corresponds to',related_users)

执行结果

a corresponds to  {'B', 'A'}
B u in users
B v in uers
A v in uers
{'B': {'A': 1}}
A u in users
B v in uers
{'B': {'A': 1}, 'A': {'B': 1}}
A v in uers
b corresponds to  {'C', 'A'}
C u in users
C v in uers
A v in uers
{'B': {'A': 1}, 'A': {'B': 1}, 'C': {'A': 1}}
A u in users
C v in uers
{'B': {'A': 1}, 'A': {'B': 1, 'C': 1}, 'C': {'A': 1}}
A v in uers
d corresponds to  {'D', 'A'}
D u in users
D v in uers
A v in uers
{'B': {'A': 1}, 'A': {'B': 1, 'C': 1}, 'C': {'A': 1}, 'D': {'A': 1}}
A u in users
D v in uers
{'B': {'A': 1}, 'A': {'B': 1, 'C': 1, 'D': 1}, 'C': {'A': 1}, 'D': {'A': 1}}
A v in uers
c corresponds to  {'B', 'D'}
B u in users
B v in uers
D v in uers
{'B': {'A': 1, 'D': 1}, 'A': {'B': 1, 'C': 1, 'D': 1}, 'C': {'A': 1}, 'D': {'A': 1}}
D u in users
B v in uers
{'B': {'A': 1, 'D': 1}, 'A': {'B': 1, 'C': 1, 'D': 1}, 'C': {'A': 1}, 'D': {'A': 1, 'B': 1}}
D v in uers
e corresponds to  {'C', 'D'}
C u in users
C v in uers
D v in uers
{'B': {'A': 1, 'D': 1}, 'A': {'B': 1, 'C': 1, 'D': 1}, 'C': {'A': 1, 'D': 1}, 'D': {'A': 1, 'B': 1}}
D u in users
C v in uers
{'B': {'A': 1, 'D': 1}, 'A': {'B': 1, 'C': 1, 'D': 1}, 'C': {'A': 1, 'D': 1}, 'D': {'A': 1, 'B': 1, 'C': 1}}
D v in uers

输出N[]:
B corresponds to 2
A corresponds to 3
C corresponds to 2
D corresponds to 3

输出C[][]:
B corresponds to {'A': 1, 'D': 1}
A corresponds to {'B': 1, 'C': 1, 'D': 1}
C corresponds to {'A': 1, 'D': 1}
D corresponds to {'A': 1, 'B': 1, 'C': 1}

p46 计算最终相似度矩阵W(cal)

#3、calculate final similarity matrix W :计算最终相似度矩阵W
W = dict()
#下面的两层迭代其实即C[u][v]=cuv。
for u,related_users in C.items():#u为一级坐标
	for v,cuv in related_users.items():#v为二级坐标,cuv即C[u][v]的值,即|N(u)∩N(v)|
		if u not in W.keys():
			W.update({u:{v:cuv/math.sqrt(N[u]*N[v])}})
		else:
			W[u].update({v:cuv/math.sqrt(N[u]*N[v])})

		print(W)
		
for u,related_users in W.items():
		print(u,'corresponds to ',related_users)

执行结果

{'B': {'A': 0.4082482904638631}}
{'B': {'A': 0.4082482904638631, 'D': 0.4082482904638631}}
{'B': {'A': 0.4082482904638631, 'D': 0.4082482904638631}, 'A': {'B': 0.4082482904638631}}
{'B': {'A': 0.4082482904638631, 'D': 0.4082482904638631}, 'A': {'B': 0.4082482904638631, 'C': 0.4082482904638631}}
{'B': {'A': 0.4082482904638631, 'D': 0.4082482904638631}, 'A': {'B': 0.4082482904638631, 'C': 0.4082482904638631, 'D': 0.3333333333333333}}
{'B': {'A': 0.4082482904638631, 'D': 0.4082482904638631}, 'A': {'B': 0.4082482904638631, 'C': 0.4082482904638631, 'D': 0.3333333333333333}, 'C': {'A': 0.4082482904638631}}
{'B': {'A': 0.4082482904638631, 'D': 0.4082482904638631}, 'A': {'B': 0.4082482904638631, 'C': 0.4082482904638631, 'D': 0.3333333333333333}, 'C': {'A': 0.4082482904638631, 'D': 0.4082482904638631}}
{'B': {'A': 0.4082482904638631, 'D': 0.4082482904638631}, 'A': {'B': 0.4082482904638631, 'C': 0.4082482904638631, 'D': 0.3333333333333333}, 'C': {'A': 0.4082482904638631, 'D': 0.4082482904638631}, 'D': {'A': 0.3333333333333333}}
{'B': {'A': 0.4082482904638631, 'D': 0.4082482904638631}, 'A': {'B': 0.4082482904638631, 'C': 0.4082482904638631, 'D': 0.3333333333333333}, 'C': {'A': 0.4082482904638631, 'D': 0.4082482904638631}, 'D': {'A': 0.3333333333333333, 'B': 0.4082482904638631}}
{'B': {'A': 0.4082482904638631, 'D': 0.4082482904638631}, 'A': {'B': 0.4082482904638631, 'C': 0.4082482904638631, 'D': 0.3333333333333333}, 'C': {'A': 0.4082482904638631, 'D': 0.4082482904638631}, 'D': {'A': 0.3333333333333333, 'B': 0.4082482904638631, 'C': 0.4082482904638631}}
输出W[][]:
B corresponds to  {'A': 0.4082482904638631, 'D': 0.4082482904638631}
A corresponds to  {'B': 0.4082482904638631, 'C': 0.4082482904638631, 'D': 0.3333333333333333}
C corresponds to  {'A': 0.4082482904638631, 'D': 0.4082482904638631}
D corresponds to  {'A': 0.3333333333333333, 'B': 0.4082482904638631, 'C': 0.4082482904638631}

P47 计算用户user,对,自己没有过行为的物品,的感兴趣的程度

#4 计算用户A对,自己没有过行为的物品,的感兴趣程度
def itemgetter(elem):
    return elem[1]

# 给用户A进行推荐,user = 'A'
user = 'A'
K = 3
rank = dict()
interacted_items = train[user]
for v, wuv in sorted(W[user].items(), key=itemgetter, reverse=True)[0:K]:
    for i, rvi in train[v].items():
        if i in interacted_items:
        	#即如果物品i,在用户user(此处为A)的,有过行为的列表中,(换句话说,用户user对物品i有过行为),将物品i过滤掉,不计算感兴趣程度。
        	continue;
        else:
        	if i not in rank.keys():
        		rank.update({i:0})
        	rank[i] += wuv*rvi

# 得到结果rank
print('输出rank[]:')
print(rank)

执行结果:

输出rank[]:
{'c': 0.7415816237971964, 'e': 0.7415816237971964}

2 用户相似度计算的改进

Python中的math.log()函数默认以math.e为底数。[5]
经测试

>>> import math
>>> math.log(math.e)
1.0

参考文献

[1] Python的二维字典(two-dimension dictionary)定义与实现方法
[2]《推荐系统实践》项亮
[3]《Python基础教程 (第三版)》
[4]《流畅的Python》Luciano Ramalho
[5] math不写底数时,底数到底是多少?-知乎

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
# -*- coding: utf-8 -*- import pandas as pd import numpy as np from math import sqrt critics={'Lisa Rose': {'Lady in the Water': 2.5, 'Snakes on a Plane': 3.5, 'Just My Luck': 3.0, 'Superman Returns': 3.5, 'You, Me and Dupree': 2.5, 'The Night Listener': 3.0}, 'Gene Seymour': {'Lady in the Water': 3.0, 'Snakes on a Plane': 3.5, 'Just My Luck': 1.5, 'Superman Returns': 5.0, 'The Night Listener': 3.0, 'You, Me and Dupree': 3.5}, 'Michael Phillips': {'Lady in the Water': 2.5, 'Snakes on a Plane': 3.0, 'Superman Returns': 3.5, 'The Night Listener': 4.0}, 'Claudia Puig': {'Snakes on a Plane': 3.5, 'Just My Luck': 3.0, 'The Night Listener': 4.5, 'Superman Returns': 4.0, 'You, Me and Dupree': 2.5}, 'Mick LaSalle': {'Lady in the Water': 3.0, 'Snakes on a Plane': 4.0, 'Just My Luck': 2.0, 'Superman Returns': 3.0, 'The Night Listener': 3.0, 'You, Me and Dupree': 2.0}, 'Jack Matthews': {'Lady in the Water': 3.0, 'Snakes on a Plane': 4.0, 'The Night Listener': 3.0, 'Superman Returns': 5.0, 'You, Me and Dupree': 3.5}, 'Toby': {'Snakes on a Plane':4.5,'You, Me and Dupree':1.0,'Superman Returns':4.0}} df_critics=pd.DataFrame(critics) ##欧氏距离 def sim_distance(prefs,person1,person2): si={} for item in prefs[person1]: if item in prefs[person2]: si[item]=1 if len(si)==0: return 0 sum_of_squares=sum([pow(prefs[person1][item]-prefs[person2][item],2) for item in prefs[person1] if item in prefs[person2]]) return 1/(1+sqrt(sum_of_squares)) ##numpy pandas 方法 def sim_distance2(prefs,person1,person2): return 1/(1+np.linalg.norm(prefs[person1]-prefs[person2])) ##皮尔逊相关系数 def sim_pearson(prefs,p1,p2): si={} for item in prefs[p1]: if item in prefs[p2]: si[item]=1 n=len(si) if n==0: return 1 ##对所有偏好求和 sum1=sum([prefs[p1][it] for it in si]) sum2=sum([prefs[p2][it] for it in si]) ##求平方和 sum1Sq=sum([pow(prefs[p1][it]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

张之海

若有帮助,客官打赏一分吧

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值