基于上下文的推荐 -- 包括时间衰减算法和位置推荐算法(代码实现)
Posted mishidemudong
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于上下文的推荐 -- 包括时间衰减算法和位置推荐算法(代码实现)相关的知识,希望对你有一定的参考价值。
基于上下文的推荐
基于时间特征的推荐
时间衰减
基于时间衰减的ItemCF算法
算法核心两部分,都加入了时间衰减项
以movielens数据集实现ItemCF
基于时间衰减的UserCF算法
以movielens数据集实现UserCF
基于地点和热度推荐
原理(包含三种数据集)
以home-less数据集为例实现代码
基于时间特征的推荐
时间信息对用户兴趣的影响:
物品具有生命周期(例如春节档电影)
用户兴趣随时间变化
季节效应(冬奶茶夏圣代,吸溜)
所以在给定时间信息后,对于推荐系统变成了一个时变的系统。
对于Delicious数据集(包括4部分–用户ID,日期,网页URL和标签)我们用不同的指标可以度量网站中物品的生命周期:
物品评价在线天数
相隔T天系统物品流行度向量的平均相似度(判断用户兴趣的转变)
在这里插入图片描述
可以看到成指数级下降,即我们推荐时要降低几天前的权重。
实现实时性要求推荐算法:
要求每个用户访问时,要根据用户当前时间点前的行为实时计算推荐列表。
推荐算法要平衡考虑用户近期和长期行为(用户有长期的兴趣爱好)
对于现实中的推荐系统表现可以看出:对于推荐的书籍会在你搜索某本书籍时发生一两本的变化,但是整体还是稳定的,维持着用户长期兴趣的推荐。
通过对用户调查的实验观察得出:
在这里插入图片描述
对于没有用户行为时,实现时间多样性的方法:
在生成结果时加入随机性
记录用户每天看到的推荐结果,再次推荐时针对前几天看到很多次的推荐结果降权
(若降权后,推荐的仍在列表前面则继续推荐)
时间衰减
在这里插入图片描述
基于时间衰减的ItemCF算法
算法核心两部分,都加入了时间衰减项
时间衰减函数:在这里插入图片描述
在这里插入图片描述
以movielens数据集实现ItemCF
import json
import pandas as pd
import math
import os
from operator import itemgetter
from sklearn.model_selection import train_test_split
class NewItemCF:
def __init__(self,datafile,simfile):
self.alpha = 0.5
self.beta = 0.8
#文件目录
self.datafile = datafile
#存放相似度矩阵的文件目录
self.simfile=simfile
#最大的时间
self.max_time=self.get_maxtime()
#获得训练集与测试集
self.train, self.test = self.loadData()
if os.path.exists(simfile):
self.items_sim=json.load(open('data/items_sim.json', 'r'))
else:
self.items_sim = self.ItemSimilarityBest()
def loadData(self):
data = list()
with open(self.datafile, 'r') as f:
lines = f.readlines()
for line in lines:
userid, itemid, record, timestamp = line.split("::")
data.append((userid, itemid, int(record), int(timestamp)))
train_list, test_list = train_test_split(data, test_size=0.3,random_state=1)
train_dict = self.transform(train_list)
test_dict = self.transform(test_list)
return train_dict, test_dict
def get_maxtime(self):
title = ['user', 'movie', 'rating', 'time']
data = pd.read_csv(self.datafile, sep='::', names=title,engine = 'python')
return data['time'].max()
def transform(self,data):
data_dict=dict()
for userid,itemid,record,timestamp in data:
data_dict.setdefault(userid,{})
data_dict[userid].setdefault(itemid,{})
data_dict[userid][itemid]['rate']=record
data_dict[userid][itemid]['time']=timestamp
return data_dict
def ItemSimilarityBest(self):
items_sim=dict()
#统计每个物品的关联用户数
item_user_count=dict()
#两两物品相似度计算的分子部分
C=dict()
for user,items in self.train.items():
for i in items.keys():
item_user_count.setdefault(i,0)
if self.train[user][i]['rate']>0:
item_user_count[i]+=1
if i not in C.keys():
C[i]=dict()
for j in items.keys():
if i==j:
continue
if j not in C[i].keys():
C[i][j]=0
if self.train[user][i]['rate']>0 and self.train[user][j]['rate']>0:
C[i][j]+=1/(1+self.alpha*abs(self.train[user][i]['time']-self.train[user][j]['time'])/(24*60*60))
for i,related_items in C.items():
items_sim.setdefault(i,dict())
for j,cij in related_items.items():
items_sim[i][j]=cij/math.sqrt(item_user_count[i]*item_user_count[j])
json.dump(items_sim, open(self.simfile, 'w'))
return items_sim
def recommand(self,user,K=20,N=10):
items_sim=self.items_sim
rank=dict()
ru=self.train.get(user,{})
for i,rui in ru.items():
for j,wij in sorted(items_sim[i].items(),key=itemgetter(1),reverse=True)[:K]:
if j in ru.keys():
continue
if j not in rank.keys():
rank[j]=0.0
rank[j]+=rui['rate']*wij*(1/(1+self.beta*(self.max_time-rui['time'])/(24*60*60)))
return sorted(rank.items(),key=itemgetter(1),reverse=True)[:N]
def precision(self, K=20, N=10):
hit = 0
num=0
for user in self.train.keys():
tu = self.test.get(user, {})
rank = self.recommand(user, K=K, N=N)
for item, rate in rank:
if item in tu:
hit += 1
num += N
precision=hit/num
return precision
if __name__ == '__main__':
b=NewItemCF('ml-1m/ratings.dat','data/items_sim.json')
print(b.precision())
基于时间衰减的UserCF算法
原理同上面的ItemCF算法,这里不再解释。
以movielens数据集实现UserCF
import json
import math
import pandas as pd
import os
from operator import itemgetter
from sklearn.model_selection import train_test_split
class NewUserCF:
def __init__(self,datafile,simfile):
self.alpha=0.5
self.beta=0.8
#文件目录
self.datafile=datafile
#存放相似度矩阵的文件
self.simfile=simfile
#获取最大的时间
self.max_time=self.get_maxtime()
#获取数据
self.train,self.test=self.loadData()
#用户之间相似度
if os.path.exists('data/users_sim.json'):
self.users_sim=json.load(open('data/users_sim.json','r'))
else:
self.users_sim=self.UsersSimilarity()
def get_maxtime(self):
title = ['user', 'movie', 'rating', 'time']
data = pd.read_csv(self.datafile, sep='::', names=title, engine='python')
return data['time'].max()
def loadData(self):
data=list()
with open(self.datafile,'r') as f:
lines=f.readlines()
for line in lines:
userid,itemid,record,timestamp=line.split("::")
data.append([userid,itemid,int(record),int(timestamp)])
train_data,test_data=train_test_split(data,test_size=0.3,random_state=1)
train_data=self.transform(train_data)
test_data=self.transform(test_data)
return train_data,test_data
def transform(self,data):
data_dict=dict()
for userid,itemid,record,timestamp in data:
if userid not in data_dict.keys():
data_dict[userid]={}
if itemid not in data_dict[userid].keys():
data_dict[userid][itemid]={}
data_dict[userid][itemid]['rate']=record
data_dict[userid][itemid]['time']=timestamp
return data_dict
def UsersSimilarity(self):
#物品-用户倒查表
item_users=dict()
for u,items in self.train.items():
for i in items.keys():
item_users.setdefault(i,set())
if self.train[u][i]['rate']>0:
item_users[i].add(u)
#计算两两用户相似的分子部分
C=dict()
#统计每个用户评价过多少个电影
N=dict()
for user,item_dict in self.train.items():
if user not in N.keys():
N[user]=0
items=[item for item in item_dict.keys() if item_dict[item]['rate']>0]
N[user]=len(items)
for item,users in item_users.items():
for u in users:
C.setdefault(u,dict())
for v in users:
C[u].setdefault(v,0.0)
if v==u:
continue
C[u][v]+=(1/(1+self.alpha*abs(self.train[u][item]['time']-self.train[v][item]['time'])/(24*60*60)))*(1/math.log(1+len(users)))
users_sim=dict()
for u,related_users in C.items():
users_sim.setdefault(u,dict())
for v,wuv in related_users.items():
if u==v:
continue
users_sim[u][v]=wuv/math.sqrt(N[u]*N[v])
json.dump(users_sim,open('data/users_sim.json','w'))
return users_sim
def recommand(self,user,K=20,N=10):
"""
:param user: 用户id
:param K: 取和user相似的前K的其他用户
:param N: 推荐N个物品
:return: 推荐列表及用户对其的兴趣的字典
"""
rank=dict()
related_items=self.train.get(user,{})
for v,wuv in sorted(self.users_sim[user].items(),key=itemgetter(1),reverse=True)[:K]:
for i,rvi in self.train[v].items():
if i in related_items.keys():
continue
if i not in rank.keys():
rank[i]=0.0
else:
rank[i]+=wuv*rvi['rate']*(1/(1+self.beta*(self.max_time-rvi['time'])))
return sorted(rank.items(),key=itemgetter(1),reverse=True)[:N]
def precision(self,K=20,N=10):
hit=0
num=0
for user in self.train.keys():
tu=self.test.get(user,{})
rank=self.recommand(user,K=K,N=N)
for item,rate in rank:
if item in tu:
hit+=1
num+=N
precision=hit/num
return precision
if __name__ == '__main__':
a=NewUserCF('ml-1m/ratings.dat','data/users_sim.json')
print(a.precision())
基于地点和热度推荐
原理(包含三种数据集)
在这里插入图片描述
以home-less数据集为例实现代码
# 这里用了老师给的代码
# 这个数据集与上面三种数据集采用的思想不一样
import pandas as pd
class RecBasedAh:
def __init__(self,path=None,Addr='朝阳区',type='score',k=10):
self.path=path
self.Addr=Addr
self.type=type
self.k=k
self.data=self.load_mess()
def load_mess(self):
# 这个函数筛选出用户位置周围的数据
data=pd.read_csv(self.path,header=0,sep=',',encoding='GBK')
return data[data['addr']==self.Addr]
def recommand(self):
# 判断推荐所依据的原因
# else 中是综合原因 对于评分 评论条数 开业时间 装修时间分别做了加权
# 可以自己设计自己的要求 比如对于开业时间等不做考虑
if self.type in ['score','comment_num','lowest_price','decoration_time','open_time']:
data=self.data.sort_values(by=[self.type,'lowest_price'],ascending=False)[:self.k]
return dict(data.filter(items=['name',self.type]).values)
elif self.type=='combine':
data=self.data.filter(items=['name','score','comment_num','lowest_price','decoration_time','open_time'])
#装修时间越近越好
data['decoration_time']=data['decoration_time'].apply(lambda x:int(x)-2017)
#开业时间越早越好
data['open_time']=data['open_time'].apply(lambda x:2017-int(x))
for col in data.keys():
if col!='name':
data[col]=(data[col]-data[col].min())/(data[col].max())
data[self.type]=1*data['score']+2*data['comment_num']+1.5*data['lowest_price']+0.5*data['decoration_time']+0.5*data['open_time']
data=data.sort_values(by=self.type,ascending=False)[:self.k]
return dict(data.filter(items=['name',self.type]).values)
if __name__ == '__main__':
path='hotel-mess/hotel-mess.csv'
hotel_rec=RecBasedAh(path,Addr='朝阳区',type='combine',k=10,sort=False)
print(hotel_rec.recommand())
以上是关于基于上下文的推荐 -- 包括时间衰减算法和位置推荐算法(代码实现)的主要内容,如果未能解决你的问题,请参考以下文章