信息检索实验3-IR Evaluation

实验数据

lab3实验数据:lab3实验数据

实验要求

实验内容

实现以下指标评价,并对Experiment2的检索结果进行评价

• Mean Average Precision (MAP)

• Mean Reciprocal Rank (MRR)

• Normalized Discounted Cumulative Gain (NDCG)

实验目的

理解MAP(平均精度均值)、MRR(平均倒数排名)、NDCG(归一化折损累积增益)算法,并且在已有数据上编程实现

实验环境

Windows10、python3.8、Spyder(Anaconda3)

实验基本原理相关算法详解

MAP:平均精度值均值

什么是AP(平均精度值)呢?

那么回顾Precision和Recall。

Precision: 即精确率,检索最相关的顶级文档的能力.

Recall: 即召回率, 检索到语料库中所有相关项的能力。

精确率=检索到的相关文档数量/检索的文档总量, 召回率=检索到的相关文档数量/总的相关文档数量

figure

为什么不用准确率呢?
准确率,也就是accuracy,也就是系统究竟做对了多少。如果是对于平衡样本来说,这
个没问题。但是对于样本不平衡的情况,这就不行。例如信息检索,有99.999%的信息
都对用户没用,而且大部分系统肯定都能判别出来这些没用,所以对于绝大部分系统,
准确率都是99.999%,这个就不能很好的衡量系统性能了。

figure
F-measure

但是我们还是希望有一个数能够衡量系统的性能,否则系统A的precision比系统B高,但
是recall却比系统B低,那么我们就不太好选了。所以综合Precision和Recall,我们得到
一个F-Score.这个F Score是P和R的调和平均数(harmonic mean),β 的作用是表明P与R
谁比较重要。harmonic mean有个特点,就是假如其中一个P或者R比较小,那么整体就
会很小,也就是对两个数中如果有特别小的数的惩罚(penalty)比较大。这是代数平均
数所没有的特性。一般来说,会把β设置成1,这个时候就成为F1-Score.

figure
MAP 计算例子
前面说了一堆概念,不如直接上例子,如下图,两次查询的精确率和召回率解释。

figure
计算方法:
就拿rank1来讲,第一个返回的是正确结果,而且只有一个,所以
precision=1,recall=1/5,
因为有5个相关文档,此时已经检索到了1个,第二个红色,表示它其实不是相关文档,那么正确的还是只有一个,所以
recall=1/5,而precision=0.5,
因为此刻返回来2个相关项,一个正确,一个错误,再看第三个,返回的是相关项,所以
recall=2/5,precision=2/3.
后面的按照此原理可以推导出来。接下来计算MAP

average precision query1=(1.0+0.67+0.5+0.44+0.5)/5=0.62;

average precision query2=(0.5+0.4+0.43)/3=0.44;

mean average precision=(ap1+ap2)/2=0.53;

MAP即是算出所有查询相关项的平均的精确度,再取所有查询的精确度的平均值。

Codes

def MAP_eval(qrels_dict, test_dict, k = 100):
    AP_result = [] #返回每一个类对应的AP
    for query in qrels_dict:
        test_result = test_dict[query]
        true_list = set(qrels_dict[query].keys()) #真阳序列表
        true_length = len(test_result) 
        if true_length <= 0:
            print('query:', query, ' not found test list')
            return []
        P_result = [] #每一个词的准确率
        i = 0
        rank_yes = 0  #序列表中正确的term
        for doc_id in test_result[0: true_length]:
            i += 1
            if doc_id in true_list:
                rank_yes += 1
                P_result.append(rank_yes / i)
        if P_result:
            AP = np.sum(P_result) / len(true_list)
            AP_result.append(AP)
        else:
            print('query:', query, ' not found a true value')
            AP_result.append(0)
    return np.mean(AP_result) #对所有类求平均

Mean Reciprocal Rank (MRR):平均倒数排名

MRR概念

MRR得核心思想很简单:返回的结果集的优劣,跟第一个正确答案的位置有关,第一个正确答案越靠前,结果越好。具体来说:对于一个query,若第一个正确答案排在第n位,则MRR得分就是 1/n 。(如果没有正确答案,则得分为0)
figure

Q为样本query集合,|Q|表示Q中query个数, R a n k i Rank_i Ranki 表示在第i个query中,第一个正确答案的排名

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

Code

#平均倒数排名,对比MAP,当返回的相关结果较少时,使用它更加合适
def MRR_eval(qrels_dict, test_dict, k = 100):#根据置信度排序,分高的在前面
#对于一个query,若第一个正确答案排在第n位,那么MRR得分就是1/n
 #   公式1/QΣ(1/rank)

    R_Score = []
    for query in qrels_dict:
        test_result = test_dict[query]
        true_list = set(qrels_dict[query].keys())
        true_length = len(test_result)
        if true_length <= 0:
            print('query ', query, ' not found test list')
            return []
        P_result = []#正确结果
        k = 0
        for doc_id in test_result[0: true_length]:
            k += 1
            if doc_id in true_list:
                P_result.append(1 / k) #得分1/k,并求和
                break
        if P_result:
            RR = np.sum(P_result)/1.0
            R_Score.append(RR)
        else:
            R_Score.append(0)
    return np.mean(R_Score)

NDCG(归一化折损累积增益)

NDCG,Normalized Discounted cumulative gain 归一化折损累计增益,这个指标通常是用来衡量和评价搜索结果算法

DCG的两个思想:

1、高关联度的结果比一般关联度的结果更影响最终的指标得分;

2、有高关联度的结果出现在更靠前的位置的时候,指标会越高;

累计增益(CG)
figure

r e l i rel_i reli 代表i这个位置上的相关度

折损累计增益(DCG
figure
归一化折损累计增益
先计算理想状态下的的DCG
figure
然后做归一化
figure
NDCG计算例子
1.计算CG
figure
2.计算DCG
figure
4.计算IDCG
在这里插入图片描述
5.计算NDCG
figure
Codes

def NDCG_eval(qrels_dict, test_dict, k = 100):
    NDCG_result = []
    for query in qrels_dict:
        test_result = test_dict[query]
        true_list = list(qrels_dict[query].values())
        true_list = sorted(true_list, reverse=True)
        i = 1
        DCG = 0.0 #在每一个CG的结果上除以一个折损值
        IDCG = 0.0
        true_length =len(true_list)
        if true_length <= 0:
            print('query ', query, ' not found test list')
            return []
        for doc_id in test_result[1: true_length]:
            i += 1
            rel = qrels_dict[query].get(doc_id, 0)
            DCG += rel / math.log(i, 2)
            IDCG += true_list[i-2] / math.log(i, 2)
        rel1=qrels_dict[query].get(test_result[0], 0)
        DCG += rel1
        NDCG = DCG / IDCG #归一化处理
        NDCG_result.append(NDCG)
    return np.mean(NDCG_result)

实验结果图

figure

代码

(evaluation.py)

import math
import numpy as np

def generate_tweetid_gain(file_name):
    qrels_dict = {}
    with open(file_name, 'r', errors='ignore') as f:
        for line in f:
            ele = line.strip().split(' ')
            if ele[0] not in qrels_dict:
                qrels_dict[ele[0]] = {}
            # here we want the gain of doc_id in qrels_dict > 0,
            # so it's sorted values can be IDCG groundtruth
            if int(ele[3]) > 0:
                qrels_dict[ele[0]][ele[2]] = int(ele[3])
    return qrels_dict

def read_tweetid_test(file_name):
    # input file format
    # query_id doc_id
    # query_id doc_id
    # query_id doc_id
    # ...
    test_dict = {}
    with open(file_name, 'r', errors='ignore') as f:
        for line in f:
            ele = line.strip().split(' ')
            if ele[0] not in test_dict:
                test_dict[ele[0]] = []
            test_dict[ele[0]].append(ele[1])
    return test_dict
#平均精度均值
def MAP_eval(qrels_dict, test_dict, k = 100):
    AP_result = [] #返回每一个类对应的AP
    for query in qrels_dict:
        test_result = test_dict[query]
        true_list = set(qrels_dict[query].keys()) #真阳序列表
        true_length = len(test_result) 
        if true_length <= 0:
            print('query:', query, ' not found test list')
            return []
        P_result = [] #每一个词的准确率
        i = 0
        rank_yes = 0  #序列表中正确的term
        for doc_id in test_result[0: true_length]:
            i += 1
            if doc_id in true_list:
                rank_yes += 1
                P_result.append(rank_yes / i)
        if P_result:
            AP = np.sum(P_result) / len(true_list)
            AP_result.append(AP)
        else:
            print('query:', query, ' not found a true value')
            AP_result.append(0)
    return np.mean(AP_result) #对所有类求平均
#平均倒数排名,对比MAP,当返回的相关结果较少时,使用它更加合适
def MRR_eval(qrels_dict, test_dict, k = 100):#根据置信度排序,分高的在前面
#对于一个query,若第一个正确答案排在第n位,那么MRR得分就是1/n
 #   公式1/QΣ(1/rank)

    R_Score = []
    for query in qrels_dict:
        test_result = test_dict[query]
        true_list = set(qrels_dict[query].keys())
        true_length = len(test_result)
        if true_length <= 0:
            print('query ', query, ' not found test list')
            return []
        P_result = []#正确结果
        k = 0
        for doc_id in test_result[0: true_length]:
            k += 1
            if doc_id in true_list:
                P_result.append(1 / k) #得分1/k,并求和
                break
        if P_result:
            RR = np.sum(P_result)/1.0
            R_Score.append(RR)
        else:
            R_Score.append(0)
    return np.mean(R_Score)
#允许以实数形式进行相关性打分,归一化折损累积增益
def NDCG_eval(qrels_dict, test_dict, k = 100):
    NDCG_result = []
    for query in qrels_dict:
        test_result = test_dict[query]
        true_list = list(qrels_dict[query].values())
        true_list = sorted(true_list, reverse=True)
        i = 1
        DCG = 0.0 #在每一个CG的结果上除以一个折损值
        IDCG = 0.0
        true_length =len(true_list)
        if true_length <= 0:
            print('query ', query, ' not found test list')
            return []
        for doc_id in test_result[1: true_length]:
            i += 1
            rel = qrels_dict[query].get(doc_id, 0)
            DCG += rel / math.log(i, 2)
            IDCG += true_list[i-2] / math.log(i, 2)
        rel1=qrels_dict[query].get(test_result[0], 0)
        DCG += rel1
        NDCG = DCG / IDCG #归一化处理
        NDCG_result.append(NDCG)
    return np.mean(NDCG_result)

def evaluation():
    k = 100
    # query relevance file
    file_qrels_path = 'qrels.txt'
    # qrels_dict = {query_id:{doc_id:gain, doc_id:gain, ...}, ...}
    qrels_dict = generate_tweetid_gain(file_qrels_path)
    # ur result, format is in function read_tweetid_test, or u can write by ur own
    file_test_path = 'result.txt'
    # test_dict = {query_id:[doc_id, doc_id, ...], ...}
    test_dict = read_tweetid_test(file_test_path)
    MAP = MAP_eval(qrels_dict, test_dict, k)
    print('MAP', ' = ', round(MAP,3), sep='')
    MRR = MRR_eval(qrels_dict, test_dict, k)
    print('MRR', ' = ', round(MRR,3), sep='')
    NDCG = NDCG_eval(qrels_dict, test_dict, k)
    print('NDCG', ' = ', round(NDCG,3), sep='')

if __name__ == '__main__':
    evaluation()
  • 0
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值