本文实验探索了评价查询好坏的MAP,MRR,IDCG三种方式,从原理到代码以及使用方式做介绍,如果对您有帮助,欢迎点赞 : )
实验过程
实验数据分析:
qrels.txt:(标准查询与文档以及相关性 对照文档)
result.txt(本地查询方法结果):
Mean Average Precision (MAP)
对于寻返回一个排序的结果,其中有相关的(绿色),不相关(红色),Precision表示到当前rank之前的正确的所占的比例;
AP:平均精准率,将rank中正确的所对应Presion拿出来取平均值,得到这个query的AP;
MAP:对于所有query的ap求平均值,得到查询的平均精确度MAP;
def MAP_eval(qrels_dict,res_dict):
APs=[]#每个query的AP
for query in qrels_dict:#对于每个query
true_ans=qrels_dict[query]#真实相关文档
search_res=res_dict[query]#查询返回值
accumulate_num=0#累计数量
true_num=0#正确的个数
per_rank=[]#一个query的返回中的Presions
for doc in search_res:
accumulate_num+=1
if doc in true_ans:
true_num+=1
per_rank.append(float(true_num/accumulate_num))
tsum=0#计算一个query的AP
tcnt=0
for per_r in per_rank:
tsum+=per_r
tcnt+=1
APs.append(float(tsum/tcnt))
tsum=0#计算MAP
tcnt=0
for ap in APs:
tsum+=ap
tcnt+=1
ans=float(tsum/tcnt)
return ans
Mean Reciprocal Rank (MRR):平均倒数排名
MRR概念
MRR得核心思想很简单:返回的结果集的优劣,跟第一个正确答案的位置有关,第一个正确答案越靠前,结果越好。具体来说:对于一个query,若第一个正确答案排在第n位,则MRR得分就是 1/n 。(如果没有正确答案,则得分为0)
MRR计算例子
假设现在有5个query语句,q1、q2、q3、q4、q5
q1的正确结果在第4位,q2和q3没有正确结果(即未检索到文档),q4正确结果在第5位,q5正确结果在第10位。
那么系统的得分就是1/4+0+0+1/5+1/10=0.55
最后MRR就是求平均,即该系统MRR=0.55/5=0.11
def MRR_eval(qrels_dict,res_dict):
mrrs=[]#所有query的rr
for query in qrels_dict:
true_docs=qrels_dict[query]#实际相关
search_ans=res_dict[query]#查询返回结果
acc_rank=0#累计rank
for per_doc in search_ans:
acc_rank+=1
if per_doc in true_docs:#找到正确的后记录返回
mrrs.append(float(1/acc_rank))
break
tsum=0.0#计算MRR
tcnt=0
for rr in mrrs:
tsum+=rr
tcnt+=1
return float(tsum/tcnt)
Normalized Discounted Cumulative Gain (NDCG):(归一化折损累积增益)
Gain
表示每篇文档的重要性,对应0/1/2一个gain增益;也用rel表示;
CG:
累计增益,
表示到当前排名的所有文档的增益;
DCG:
返回的顺序也会对查询结果有影响,上面没有考虑位置因素,增加一个折损考虑位置:
NDCG:
查询结果容易受返回结果的长短的影响,返回的越长可能导致DCG越大,不同长度的query结果没有直接的比较意义,为了增加比较意义,更便于直接比较,引入归一化,按照全部的正确结果排在最前面且按照增益降序排列得到最优IDCG,然后将DCG/IDCG作为NDCG;
计算CG:
计算DCG:
计算IDCG:
得到的NDCG就可以在0–1之间;
其中公式:
def NDCG_eval(qrels_dict_rel,res_dict):#这里需要rel数值
ndcgs=[]
# print(qrels_dict_rel)
for per_query in qrels_dict_rel.keys():
true_ranks=qrels_dict_rel[per_query]#真正的相关文档和rel值为 dic
#len_true=len(true_ranks)#算DCG的终止位置
# print(true_ranks)
sorted_true_ranks=sorted(true_ranks,reverse=True)
# print(true_ranks)
IDCG=0.0
i=2
IDCG+=int(true_ranks[sorted_true_ranks[0]])#初始值是否和DCG相同
for per_item in sorted_true_ranks[1:]:
gain=true_ranks[per_item]
IDCG+=int(gain)/float(math.log(i,2))
i+=1
DCG=0.0
i=2
search_res=res_dict[per_query]
if search_res[0] in true_ranks:#先判断在相关文档中
DCG+=int(true_ranks[search_res[0]])
for per_res in search_res[1:]:#算法的位置?1:lentrue
if per_res in true_ranks.keys():
DCG+=int(true_ranks[per_res])/float(math.log(i,2))
i+=1
# print(DCG/IDCG)
ndcgs.append(DCG/IDCG)
# tnum=0.0
tsum=0.0
for ndcg in ndcgs:
# tnum+=1
tsum+=ndcg
return float(tsum)/len(ndcgs)
限定对前100返回结果进行评价:
def evaluate(k):#按照排名前100查询
qrels_file="qrels.txt"
res_file="result.txt"
qrels_dict={}#所有的查询项正确结果
qrels_dict_rel={}#查询项正确结果以及rel
res_dict={}#自己查询实际返回结果
with open(qrels_file,"r") as f:#将真确结果填充
for line in f:
evals=line.strip().split(" ")
if evals[0] not in qrels_dict:
qrels_dict[evals[0]]=[]
qrels_dict_rel[evals[0]]={}
if int(evals[3])>0:#只保留真正只有rel>0才是相关项存储,后面不用判断rel为正
qrels_dict[evals[0]].append(evals[2])
qrels_dict_rel[evals[0]][evals[2]]=evals[3]
# print(qrels_dict)
with open(res_file,"r") as f:#将查询返回结果保存为{qiery:[d1,d2,...],q2:[d1,d2,...]}
for line in f:
item=line.strip().split(' ')
if item[0] not in res_dict:
res_dict[item[0]]=[]
res_dict[item[0]].append(item[1])
for key in res_dict.keys():
res_dict[key]=res_dict[key][:k]
# print("key:",key,"len_ans:",len(res_dict[key]))
# print(res_dict,"----------")
map_eval=MAP_eval(qrels_dict,res_dict)
mrr_eval=MRR_eval(qrels_dict,res_dict)
ndcg_eval=NDCG_eval(qrels_dict_rel,res_dict)
print("map_eval:",map_eval,"\nmrr_eval:",mrr_eval,"\nndcg_eval:",ndcg_eval)
实验结果
上述没有限定在返回的前多少个中做评价,限定了在返回结果的前100中的结果:
实验总结
了解了几种评价查询系统优劣的方法,对于其中评判标准的不同有了进一步的认识;
其中结果MAP与NDCG结果相近,进一步探究两者:
MAP和NDCG这两种指标是位置敏感的评价指标,总体来说可以理解为:正确推荐的item在列表中越靠前,其贡献的推荐效果越大,反之,正确推荐的item在列表中越靠后,贡献的推荐效果越小。MAP和NDCG主要的区别在于,MAP考虑的时二元相关性(1和0),NDCG可以有多种相关性的比较,相关性可以按照程度有多种取值,类似于分类与回归的关系。
说明结果返回的rank trival效果较好;
MRR只关注返回结果的第一个项目;
评价
这三者都是有排序意义的度量指标;
MRR的优点
- 该方法计算简单,解释简单。
- 这种方法高度关注列表的第一个相关元素。它最适合有针对性的搜索,比如用户询问“对我来说最好的东西”。
- 适用于已知项目搜索,如导航查询或寻找事实。
MRR的缺点
- MRR指标不评估推荐项目列表的其余部分。它只关注列表中的第一个项目。
- 它给出一个只有一个相关物品的列表。如果这是评估的目标,那找个度量指标是可以的。
- 对于想要浏览相关物品列表的用户来说,这可能不是一个好的评估指标。用户的目标可能是比较多个相关物品。
MAP优点
- 给出了一个代表精确度 — 召回率曲线下复杂区域的单一度量。这提供了每个列表的平均精度。
- 处理列表推荐物品的自然排序。这与将检索项视为集合的度量标准形成了对比。
- 这一指标能够给予发生在排序高的推荐名单中的错误更多的权重。相反,它对发生在推荐列表中较深位置的错误的权重较小。这符合在推荐列表的最前面显示尽可能多的相关条目的需要。
MAP缺点
- 这个度量标准适用于二进制(相关/非相关)评级。然而,它不适合细粒度的数字评级。此度量无法从此信息中提取误差度量。
- 对于细粒度的评分,例如从1星到5星的评分,评估首先需要对评分进行阈值,以产生二元相关性。一种选择是只考虑大于4的评级。由于人工阈值的存在,这在评估度量中引入了偏差。此外,我们正在丢弃那些精细的信息。这个信息是在4星和5星之间的差异评级,以及在不相关的项目的信息。1星评级真的和3星评级一样吗?
NDCG优点
- NDCG的主要优势是它考虑到了分等级的相关性值。当它们在数据集中可用时,NDCG是一个很好的选择。
- 与MAP度量相比,它在评估排名项目的位置方面做得很好。它适用于二元的相关/非相关场景。
- 平滑的对数折现因子有一个很好的理论基础,该工作的作者表明,对于每一对显著不同的排名推荐系统,NDCG度量始终能够确定更好的一个。
NDCG缺点
- NDCG在部分反馈方面有一些问题。当我们有不完整的评级时,就会发生这种情况。这是大多数推荐系统的情况。如果我们有完整的评级,就没有真正的任务去实现!在这种情况下,recsys系统所有者需要决定如何归罪于缺失的评级。将缺少的值设置为0将把它们标记为不相关的项。其他计算值(如用户的平均/中值)也可以帮助解决这个缺点。
- 接下来,用户需要手动处理IDCG等于0的情况。当用户没有相关文档时,就会发生这种情况。这里的一个策略是也将NDCG设置为0。
推荐系统,NDCG度量始终能够确定更好的一个。
NDCG缺点
- NDCG在部分反馈方面有一些问题。当我们有不完整的评级时,就会发生这种情况。这是大多数推荐系统的情况。如果我们有完整的评级,就没有真正的任务去实现!在这种情况下,recsys系统所有者需要决定如何归罪于缺失的评级。将缺少的值设置为0将把它们标记为不相关的项。其他计算值(如用户的平均/中值)也可以帮助解决这个缺点。
- 接下来,用户需要手动处理IDCG等于0的情况。当用户没有相关文档时,就会发生这种情况。这里的一个策略是也将NDCG设置为0。
- 另一个问题是处理NDCG@K。recsys系统返回的排序列表的大小可以小于k。为了处理这个问题,我们可以考虑固定大小的结果集,并用最小分数填充较小的集合。