1、打分系统-评分误差率
def RMSE(records):
return math.sqrt(sum([(rui-pui)*(rui-pui) for u,i,rui,pui in records])/float(len(records)))
def MAE(records):
return sum([abs(rui-pui) for u,i,rui,pui in records])/float(len(records))
关于
RMSE
和
MAE
这两个指标的优缺点,
Netflix
认为
RMSE
加大了对预测不准的用户物品评
分的惩罚(平方项的惩罚),因而对系统的评测更加苛刻。研究表明,如果评分系统是基于整数
建立的(即用户给的评分都是整数),那么对预测结果取整会降低
MAE
的误差
①
。
2、Top-N推荐系统
网站在提供推荐服务时,一般是给用户一个个性化的推荐列表,这种推荐叫做
TopN
推荐。
TopN
推荐的预测准确率一般通过准确率(
precision
)
/
召回率(
recall
)度量。
令
R
(
u
)
是根据用户在训练集上的行为给用户作出的推荐列表,而
T
(
u
)
是用户在测试集上的行
为列表。那么,推荐结果的召回率和准确率分别定义为:

2.1 准确率
协同过滤算法的离线实验一般如下设计。首先,将用户行为数据集按照均匀分布随机分成
M
份(本章取
M
=8
),挑选一份作为测试集,将剩下的
M
-1
份作为训练集。然后在训练集上建立用户
兴趣模型,并在测试集上对用户行为进行预测,统计出相应的评测指标。为了保证评测指标并不
是过拟合的结果,需要进行
M
次实验,并且每次都使用不同的测试集。然后将
M
次实验测出的评
测指标的平均值作为最终的评测指标。
下面的
Python
代码描述了将数据集随机分成训练集和测试集的过程:
def splitData(self, M, k, seed=1):
'''
:params: data, 加载的所有(user, item)数据条目
:params: M, 划分的数目,最后需要取M折的平均
:params: k, 本次是第几次划分,k~[0, M)
:params: seed, random的种子数,对于不同的k应设置成一样的
:return: train, test
'''
train, test = [], []
random.seed(seed)
for user, item in self.data:
# 这里与书中的不一致,本人认为取M-1较为合理,因randint是左右都覆盖的
if random.randint(0, M-1) == k:
test.append((user, item))
else:
train.append((user, item))
# 处理成字典的形式,user->set(items)
def convert_dict(data):
data_dict = {}
for user, item in data:
if user not in data_dict:
data_dict[user] = set()
data_dict[user].add(item)
data_dict = {k: list(data_dict[k]) for k in data_dict}
return data_dict
return convert_dict(train), convert_dict(test), self.profile
这里,每次实验选取不同的
k
(
0
≤
k
≤
M
1
)和相同的随机数种子
seed
,进行
M
次实验就可
以得到
M
个不同的训练集和测试集,然后分别进行实验,用
M
次实验的平均值作为最后的评测指
标。这样做主要是防止某次实验的结果是过拟合的结果(
over fitting
),但如果数据集够大,模型
够简单,为了快速通过离线实验初步地选择算法,也可以只进行一次实验。
def Precision(train,test,N):
hit=0
all=0
for user in train.keys():
tu = test[user]
rank=GetRecommendation(user,N)#推荐物品
for item,pui in rank:
if item in tu:
hit+=1
all+=N
return hit/(all * 1.0)
2.2 召回率
def Recall(train,test,N):
hit=0
all=0
for user in train.keys():
tu = test[user]
rank = GetRecommendation(user,N)#推荐物品
for item,pui in rank:
if item in tu:
hit +=1
all += len(tu)
return hit/(all * 1.0)
2.3、覆盖率
def Coverage(train,test,N):
recommend_items=set()
all_items =set()
for user in train,keys():
for item in train[user].keys():
all_items.add(item)
rank=GetRecommendation(user,N)
for item,pui in rank:
recommend_items.add(item)
return len(recommend_items)/(len(all_items)*1.0)
2.4、多样性
2.5、其他(新颖度、惊喜度、信任度、实时性)
最后,我们还需要评测推荐的新颖度,这里用推荐列表中物品的平均流行度度量推荐结果的
新颖度。如果推荐出的物品都很热门,说明推荐的新颖度较低,否则说明推荐结果比较新颖。
def Popularity(train,test,N):
item_popularity=dict()
for user,items in train.items():
for item in items.keys():
if item not in item_popularity:
item_popularity[item]=0
item_popularity[item]+=1
ret = 0
n=0
for user in train.keys():
rank = GetRecommendation(user,N)
for item,pui in rank:
ret += math.log(1+item_popularity[item])
n+=1
ret /=n*1.0
return ret
这里,在计算平均流行度时对每个物品的流行度取对数,这是因为物品的流行度分布满足长
尾分布,在取对数后,流行度的平均值更加稳定。