背景
之前的算法主要研究了如何联系用户和物品,将最符合用户兴趣的物品推荐给用户,但这些算法都忽略了一点,就是用户所处的上下文(context)。这些上下文包括用户访问推荐系统的时间、地点、心情等。
基于时间上下文的推荐
时间信息特性
- 用户信息的变化的
- 物品也有生命周期
- 季节特性
引入时间信息后,推荐系统由静态系统变成了时变系统,用户行为变成了时间序列。
度量指标
时间多样性:推荐系统每天推荐结果的变化程度被定义为推荐系统的时间多样性。时间多样性高的推荐系统中用户会经常看到不同的推荐结果。
时间衰减
基于用户行为的时间衰减代码实现
基于movielens数据集
import pandas as pd
import numpy as np
import time
# 基于用户的协同过滤算法
class UserCF():
def __init__(self, user_item_dict):
# 将每个用户的物品取集合
self.user_item_dict = user_item_dict
# 用户列表
self.users_list = list(self.user_item_dict.keys())
# 惩罚热门物品和倒查表方式计算用户相似度(同倒查表方式一致,只是在相似度计算中引入惩罚))
def computer_similer_backform_punish(self):
import math
item_user_dict = dict()
# 建立物品到用户的倒查表
for i, items in self.user_item_dict.items():
for j in items.keys():
if j not in item_user_dict.keys():
item_user_dict[j] = list()
item_user_dict[j].append(i)
# 建立用户矩阵
self.user_matrix = np.zeros((len(self.users_list), len(self.users_list)))
for item, users in item_user_dict.items():
for user1 in users:
for user2 in users:
if user1 != user2:
# 对热门物品进行惩罚
self.user_matrix[self.users_list.index(user1)][self.users_list.index(user2)] += 1 / (
1 + 0.00008 * abs(self.user_item_dict[user1][item] - self.user_item_dict[user2][item]))
# 计算用户相似度
user_similer_list = dict()
for user1 in self.users_list:
user_similer_list[user1] = dict()
for user2 in self.users_list:
if user1 != user2:
user_similer_list[user1][user2] = round(
self.user_matrix[self.users_list.index(user1)][self.users_list.index(user2)] /
(len(self.user_item_dict[user1]) * len(self.user_item_dict[user2])) ** 0.5, 3)
self.user_matrix[self.users_list.index(user1)][self.users_list.index(user2)] = \
user_similer_list[user1][user2]
return user_similer_list, item_user_dict
# 计算指定用户的推荐物品
def compute_interest_item(self, user):
user_similer_list, item_user_dict = self.computer_similer_backform_punish()
# 取相似度高的前10名用户
sort_coor = np.argsort(self.user_matrix[self.users_list.index(user)])[::-1][:10]
recommend_item = dict()
for item in item_user_dict.keys():
recommend_item[item] = 0
for i in sort_coor:
# 推荐不在指定用户物品集中,但在相似用户物品集中的物品,r=1
if item not in self.user_item_dict[user].keys() and item in self.user_item_dict[
self.users_list[i]].keys():
recommend_item[item] += self.user_matrix[self.users_list.index(user)][i] / (
1 + 0.00008 * abs(time.time() - self.user_item_dict[self.users_list[i]][item]))
# 对推荐的物品按照感兴趣程度降序,取前10个
# print(recommend_item)
recommend_item = {k: v for k, v in sorted(recommend_item.items(), key=lambda item: item[1], reverse=True)[:10] if
v != 0}
return recommend_item
if __name__ == '__main__':
with open('.././基于上下文的推荐数据集/ml-1m/ratings.dat') as f:
data = np.array([i.strip().split('::') for i in f.readlines()])
data = pd.DataFrame(data, columns=['user_id', 'movie_id', 'rating', 'time'])
data = data[['user_id', 'movie_id', 'time']]
# data = data.sample(n = 10000,replace =False,axis = 0)
# 取前500个用户
a = list(data.groupby('user_id'))[:100]
user_dict = {}
for i in a:
user, movie = i
user_dict[user] = {list(movie['movie_id'])[i]: int(list(movie['time'])[i]) for i in range(len(movie['movie_id']))}
alpha = 0.8
usercf = UserCF(user_dict)
print('惩罚热门物品和倒查表方式计算用户相似度:')
print(usercf.computer_similer_backform_punish()[0])
print('推荐物品:')
print(usercf.compute_interest_item('1'))
推荐物品:
{'858': 5.60654359595839e-07, '541': 5.415219995890854e-07, '318': 5.245715878931347e-07, '480': 5.233550660376489e-07, '1198': 5.228485695506164e-07, '1210': 5.228454709077769e-07, '1247': 5.06476667850689e-07, '2571': 5.06393423938479e-07, '2396': 5.063891826260197e-07, '2115': 5.061229882775335e-07}
import pandas as pd
import numpy as np
import time
# 基于物品的协同过滤算法
class ItemCF():
def __init__(self, movie_dict):
self.item_user_dict = movie_dict
# 物品列表
self.items_list = list(self.item_user_dict.keys())
# 引入时间衰减和倒查表方式计算物品相似度(同倒查表方式一致,只是在相似度计算中引入惩罚))
def computer_similer_backform_punish(self):
# 建立物品到用户的倒查表
user_item_dict = dict()
for item,user in self.item_user_dict.items():
for j in user.keys():
if j not in user_item_dict.keys():
user_item_dict[j] = set()
user_item_dict[j].add(item)
# 建立物品二维矩阵
self.item_matrix = np.zeros((len(self.items_list), len(self.items_list)))
for user, items in user_item_dict.items():
for item1 in items:
for item2 in items:
if item1 != item2:
# 记录不同物品间共同用户的数目
self.item_matrix[self.items_list.index(item1)][self.items_list.index(item2)] += 1 / (
1 + 0.00008 * abs(self.item_user_dict[item1][user] - self.item_user_dict[item2][user]))
# 计算物品相似度
item_similer_list = dict()
for item1 in self.items_list:
item_similer_list[item1] = dict()
for item2 in self.items_list:
if item1 != item2:
# 利用二维矩阵存储的物品相似度进行计算
item_similer_list[item1][item2] = round(
self.item_matrix[self.items_list.index(item1)][self.items_list.index(item2)] /
(len(self.item_user_dict[item1]) * len(self.item_user_dict[item2])) ** 0.5, 3)
self.item_matrix[self.items_list.index(item1)][self.items_list.index(item2)] = \
item_similer_list[item1][item2]
# 物品相似度归一化
self.item_matrix = self.item_matrix / self.item_matrix.max(axis=1)
return item_similer_list
# 计算指定用户的推荐物品
def compute_interest_item(self, user):
# 物品相似度矩阵
item_similer_list = self.computer_similer_backform_punish()
recommend_item = dict()
for item in self.items_list:
recommend_item[item] = 0
sort_coor = np.argsort(self.item_matrix[self.items_list.index(item)])[::-1][:10]
for i in sort_coor:
# 对不在该用户的喜欢列表,但其相似物品在该用户物品列表的物品,进行计算
if user in self.item_user_dict[self.items_list[i]].keys() and user not in self.item_user_dict[item].keys():
recommend_item[item] += self.item_matrix[self.items_list.index(item)][i]/(
1 + 0.00008 * abs(time.time() - self.item_user_dict[self.items_list[i]][user]))
# 对推荐的物品按照感兴趣程度降序,前10
recommend_item = {k: v for k, v in sorted(recommend_item.items(), key=lambda item: item[1], reverse=True)[:10] if
v != 0}
return recommend_item
if __name__ == '__main__':
with open('.././基于上下文的推荐数据集/ml-1m/ratings.dat') as f:
data = np.array([i.strip().split('::') for i in f.readlines()])
data = pd.DataFrame(data, columns=['user_id', 'movie_id', 'rating', 'time'])
data = data[['user_id', 'movie_id', 'time']]
# data = data.sample(n = 10000,replace =False,axis = 0)
# 取前100个用户
a = list(data.groupby('movie_id'))[:100]
movie_dict = {}
for i in a:
movie, user = i
movie_dict[movie] = {list(user['user_id'])[i]: int(list(user['time'])[i]) for i in range(len(user['user_id']))}
itemcf = ItemCF(movie_dict)
print('惩罚活跃用户和倒查表方式计算物品相似度:')
print(itemcf.computer_similer_backform_punish())
print('推荐物品:')
print(itemcf.compute_interest_item('1'))
推荐物品:
{'858': 5.606543007337043e-07, '541': 5.415219427809389e-07, '318': 5.24571532680956e-07, '480': 5.233550110807222e-07, '1198': 5.228485147045628e-07, '1210': 5.228454160598327e-07, '1247': 5.064766144577531e-07, '2571': 5.063933705698869e-07, '2396': 5.063891292550815e-07, '2115': 5.061229349580689e-07}
基于时间段图模型的推荐
算法描述
基于地点上下文的推荐
地点作为一种重要的空间特征,也是一种重要的上下文信息。不同地区的用户兴趣有所不同,用户到了不同的地方,兴趣也会有所不同。
- 兴趣本地化:不同地方的用户兴趣存在着很大的差别。
- 活动本地化:一个用户往往在附近的地区活动。
代码实现
只是考虑了将地点作为划分,未加入用户间作用。
基于hotel-mess数据集
import numpy as np
import pandas as pd
import random
#基于地点
def recommendHotel(data,larea):
area_hotel_rank = dict()
for area,info in data:
#基于comment**(1/(1+now-decoration_time))分区域进行排序。同时考虑评论数与装修时间
popularity_list = {info['name'].iloc[i]: (info['comment_num'].iloc[i]) ** (1 / (1+(2022 - info['decoration_time'].iloc[0]))) for i in range(len(info))}
popularity_list = {k:v for k,v in sorted(popularity_list.items(),key=lambda a:a[1],reverse=True)}
area_hotel_rank[area] = popularity_list
#推荐本地区前7个,其他地区前3个
area_list = list(area_hotel_rank.keys())
area_list.remove(larea)
recommend_hotel = list(area_hotel_rank[larea])[:7]
another = [random.choice(area_list) for i in range(3)]
for i in set(another):
recommend_hotel+=list(area_hotel_rank[i])[:another.count(i)]
#随机打乱
random.shuffle(recommend_hotel)
return recommend_hotel
data = pd.read_csv('.././基于上下文的推荐数据集/hotel-mess/hotel-mess.csv',encoding='gbk')
data = data[['name','addr','comment_num','decoration_time']].groupby('addr')
print(recommendHotel(data,'朝阳区'))
推荐酒店
['国瑞百捷酒店(北京站崇文门店)', '速8酒店(北京四惠店)', '鑫奥宾馆(北京鸟巢店)', '国汉主题酒店(国展店)', '青年之家酒店', '高迪莱克优选酒店', '7天连锁酒店(北京西客站丽泽桥店)', '和家宾馆(安贞医院店)', '曲园快捷酒店', '99旅馆连锁(北京欢乐谷店)']