我正在使用pytrec_eval
来计算nDCG分数。例如,对于qrel
:
qrel = { 'q1': { 'd1': 0, 'd2': 1, 'd3': 0, }}
和run
:
run = { 'q1': { 'd1': 1.0, 'd2': 0.0, 'd3': 1.5, }}
nDCG分数可以这样计算:
import pytrec_evalimport json evaluator = pytrec_eval.RelevanceEvaluator( qrel, {'ndcg'})print(json.dumps(evaluator.evaluate(run), indent=1)) "q1": { "ndcg": 0.5 }
我的理解是nDCG考虑了检索到的文档索引的顺序,但是,如果你改变run
中的文档顺序,你仍然会得到相同的nDCG分数,例如:
run2 = { 'q1': { 'd1': 1.0, 'd3': 1.5, 'd2': 0.0, }}evaluator = pytrec_eval.RelevanceEvaluator(qrel, {'ndcg'})print(json.dumps(evaluator.evaluate(run2), indent=1))
这是计算nDCG的预期行为吗?qrel
的用途是什么?我理解qrel
告诉你检索到的文档的相关性,而run
是你的查询和信息检索系统的结果排序。那么,为什么如果我改变run
的顺序,nDCG分数还是相同的呢?
回答:
为了计算nDCG,你需要知道每个文档在该查询的排序结果列表中的相关性。这些信息包含在qrels
中。排序意味着你首先需要按照分数的降序排序检索到的文档。因此,你基本上是排序文档,然后按等级逐个进行,从最低等级开始(即得分最高的文档),等级为1。对于每个等级i,你从qrels
中获取文档的真实相关性rel_i,然后将这个相关性除以log_2(i+1)来得到这个等级i的项。你将所有这些项在所有等级上相加,就得到了这个查询的折扣累积增益(DCG)。
因此,pytrec_eval
内部需要从文档ID到分数的字典映射中创建一个排序列表,以获取等级。这就是为什么你作为输入传递的字典中文档-分数对的顺序无关紧要。现在,作为一个额外的细节:要得到nDCG(即标准化DCG),你需要将DCG除以理想DCG,这是任何模型可以达到的最大DCG;要得到IDCG,你需要按照相关性分数的降序对真实(相对于检索到的)文档进行排序,并计算DCG。同样,要获取真实相关性分数,你需要qrels
。