基于时间特征的推荐
时间信息对用户兴趣的影响:
1.物品具有生命周期(例如春节档电影)
2.用户兴趣随时间变化
3.季节效应(冬奶茶夏圣代,吸溜)
所以在给定时间信息后,对于推荐系统变成了一个时变的系统。
对于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())