当用户浏览某个物品时,标签系统非常希望用户能给这个物品打出高质量的标签,这样才能促进标签系统的良性循环。
为什么要给用户推荐标签?一般看来有以下好处。
1)方便用户输入标签。用户一般不愿意通过从键盘输入标签来给物品打标签。给用户推荐标签,可以提高用户打标签参与度。
2)提高标签质量。用户给物品打标签,同一个语义不同内容的词很庞大,而且会使计算相似度不太准确。而使用推荐标签时,我们可以对词表进行选择,首先保证词表不会出现太多的同义词,同时保证出现的词是一些比较热门的,有代表性的词。
如何给用户推荐标签,有以下四个方法。
1)PopularTags
给用户推荐整个系统中最热门的标签,这个算法太简单,以至于不能称为一种推荐算法。
def RecommendPopularTags (user,item,tags,N) :
tags=dict(sorted(tags.items(),key=lambda x:x[1 ],reverse=True )[0 :N])
return tags.keys()
2)ItemPopularTags
给用户u推荐物品i最热门的标签
def RecommendItemPopularTags (user,item,item_tags,N) :
return dict(sorted(item_tags[item].items(),key=lambda x:x[1 ],reverse=True )[0 :N]).keys()
3)UserPopularTags
给用户推荐他经常使用的标签
def RecommendUserPopularTags (user,item,user_tags,N) :
return dict(sorted(user_tags[user].items(),key=lambda x:x[1 ],reverse=True )[0 :N]).keys()
4)HybridPopularTags
前两种算法的融合,该方法通过一个系数将以上两种方法结果线性加权,然后生成最终的结果。
def RecommendHybridPopularTags (user,item,user_tags,item_tags,alpha,N) :
ret=dict()
max_user_tag_weight=max(user_tags[user].values())
for tag,weight in user_tags[user].items():
ret[tag]=(1 -alpha)*weight/max_user_tag_weight
max_item_tags_weight=max(item_tags[item].values())
for tag,weight in item_tags[item].items():
if tag not in ret:
ret[tag]=alpha*weight/max_item_tags_weight
else :
ret[tag]+=alpha*weight/max_item_tags_weight
return dict(sorted(ret.items(),key=lambda x:x[1 ],reverse=True )[0 :N]).keys()
实现完整Python代码:数据源deilicous上200509数据,我们取前5万行数据进行实验,随机分为10份,其中一份做测试,另外9份做训练。这里我们根据上述四种算法,在训练集数据中分别计算出最热门的标签,物品i最热门的标签,用户u经常使用的标签。所以在划分训练测试集时,训练集和测试集中用户,物品,标签不能有任何重复,故在这里划分训练测试集时需要以user,item,tag作为主键划分。
import pandas as pd
import numpy as np
import random
def genData () :
data=pd.read_csv('200509' ,header=None ,sep='\t' )
data.columns=['date' ,'user' ,'item' ,'tag' ]
data.drop('date' ,axis=1 ,inplace=True )
print 'genData successed!!!'
return data[:50000 ]
def user_item_tag (data) :
UIT=[]
for i in range(len(data)):
lst=list(data.loc[i])
user=lst[0 ]
item=lst[1 ]
tag=lst[2 ]
temp=(user,item,tag)
if temp not in UIT:
UIT.append(temp)
return UIT
def splitData (records,train,test) :
for user,item,tag in records:
if random.randint(1 ,10 )==1 :
test.append([user,item,tag])
else :
train.append([user,item,tag])
print 'split succeed!'
return train,test
def Initstate (data) :
tags=dict()
item_tags=dict()
user_tags=dict()
for lst in data:
user=lst[0 ]
item=lst[1 ]
tag=lst[2 ]
if tag not in tags:
tags[tag]=0
tags[tag]+=1
if item not in item_tags:
item_tags[item]=dict()
if tag not in item_tags[item]:
item_tags[item][tag]=0
item_tags[item][tag]+=1
if user not in user_tags:
user_tags[user]=dict()
if tag not in user_tags[user]:
user_tags[user][tag]=0
user_tags[user][tag]+=1
return tags,item_tags,user_tags
def RecommendPopularTags (user,item,tags,N) :
tags=dict(sorted(tags.items(),key=lambda x:x[1 ],reverse=True )[0 :N])
return tags.keys()
def RecommendItemPopularTags (user,item,item_tags,N) :
return dict(sorted(item_tags[item].items(),key=lambda x:x[1 ],reverse=True )[0 :N]).keys()
def RecommendUserPopularTags (user,item,user_tags,N) :
return dict(sorted(user_tags[user].items(),key=lambda x:x[1 ],reverse=True )[0 :N]).keys()
def RecommendHybridPopularTags (user,item,user_tags,item_tags,alpha,N) :
ret=dict()
max_user_tag_weight=max(user_tags[user].values())
for tag,weight in user_tags[user].items():
ret[tag]=(1 -alpha)*weight/max_user_tag_weight
max_item_tags_weight=max(item_tags[item].values())
for tag,weight in item_tags[item].items():
if tag not in ret:
ret[tag]=alpha*weight/max_item_tags_weight
else :
ret[tag]+=alpha*weight/max_item_tags_weight
return dict(sorted(ret.items(),key=lambda x:x[1 ],reverse=True )[0 :N]).keys()
def getTU (test,user,item) :
tags=[]
for lst in test:
t_user=lst[0 ]
t_item=lst[1 ]
t_tag=lst[2 ]
if (user==t_user) and (item==t_item):
tags.append(t_tag)
return tags
def GetRecommend (user,item,item_tags,user_tags,tags,N,state) :
if state==1 :
return RecommendItemPopularTags(user,item,item_tags,N)
if state==2 :
return RecommendPopularTags(user,item,tags,N)
if state==3 :
return RecommendUserPopularTags(user,item,user_tags,N)
if state==4 :
return RecommendHybridPopularTags(user,item,user_tags,item_tags,0.8 ,N)
def Recall (train,test,tags,item_tags,user_tags,N,a) :
'''
:param train: 训练集
:param test: 测试集
:param N: TopN推荐中N数目
:param k:
:return:返回召回率
'''
hit=0
totla=0
for lst in train:
user=lst[0 ]
item=lst[1 ]
tu=getTU(test,user,item)
rank=GetRecommend(user,item,item_tags,user_tags,tags,N,a)
for item in rank:
if item in tu:
hit+=1
totla+=len(tu)
return hit/(totla*1.0 )
def Precision (train,test,tags,item_tags,user_tags,N,a) :
'''
:param train:
:param test:
:param N:
:param k:
:return:准确率
'''
hit=0
total=0
for lst in train:
user=lst[0 ]
item=lst[1 ]
tu = getTU(test,user,item)
rank = GetRecommend(user,item,item_tags,user_tags,tags,N,a)
for item in rank:
if item in tu:
hit += 1
total += N
return hit / (total * 1.0 )
def Coverage (train,test,tags,item_tags,user_tags,N,a) :
'''
计算覆盖率
:param train:训练集 字典user->items
:param test: 测试机 字典 user->items
:param N: topN推荐中N
:param k:
:return:覆盖率
'''
recommend_items=set()
all_items=set()
for lst in train:
user=lst[0 ]
item=lst[1 ]
all_items.add(item)
rank=GetRecommend(user,item,item_tags,user_tags,tags,N,a)
for item in rank:
recommend_items.add(item)
return len(recommend_items)/(len(all_items)*1.0 )
def evaluate (train,test,tags,item_tags,user_tags,N,state) :
recall=Recall(train,test,tags,item_tags,user_tags,N,state)
precision=Precision(train,test,tags,item_tags,user_tags,N,state)
coverage=Coverage(train,test,tags,item_tags,user_tags,N,state)
return recall,precision,coverage
data=genData()
train=[]
test=[]
UIT=user_item_tag(data)
train,test=splitData(UIT,train,test)
tags,item_tags,user_tags=Initstate(train)
N=10
for stat in range(1 ,5 ):
recall,precision,coverage=evaluate(train,test,tags,item_tags,user_tags,N,stat)
print stat
print("Recall: " , recall)
print("Precision: " , precision)
print("Coverage: " , coverage)
实验结果
1
(‘Recall: ‘, 0.16858299595141701)
(‘Precision: ‘, 0.006946019883899379)
(‘Coverage: ‘, 0.6197057512260365)
2
(‘Recall: ‘, 0.10375168690958164)
(‘Precision: ‘, 0.0042748159516025)
(‘Coverage: ‘, 0.000743052459503641)
3
(‘Recall: ‘, 0.20259109311740892)
(‘Precision: ‘, 0.008347234269700406)
(‘Coverage: ‘, 0.523480457720315)
4
(‘Recall: ‘, 0.28842105263157897)
(‘Precision: ‘, 0.011883632481483953)
(‘Coverage: ‘, 0.6334522217268539)