wangyuxinwhy / uniem

unified embedding model
Apache License 2.0
829 stars 64 forks source link

T2Ranking检索任务的ground Truth(qrels)的处理是不是应该把 0 1 去掉呀 #49

Closed Wenze7 closed 1 year ago

Wenze7 commented 1 year ago

https://github.com/wangyuxinwhy/uniem/blob/9acd3ebf76bc0181fcbc4cad094acb0c4cad964b/mteb-zh/mteb_zh/tasks.py#L266 代码里好像写的是所有的档次的doc都放进了valid_qrels里。 而在MTEB的代码里,在算NDCG的IDCG的时候,只要出现在valid_qrels都算作1,其他都是0。这样那些低档的也算为正确了。 https://github.com/embeddings-benchmark/mteb/blob/582553693de507f338a720a0bc572147b8c6ef33/mteb/evaluation/evaluators/RetrievalEvaluator.py#L212 不知道是不是我的理解有问题,还是代码没考虑到?

hjq133 commented 1 year ago

这个问题我之前也思考过。 我看T2 rank对4个level的定义如下: image

只有0是不相关的,1应该也算相关吧。T2rank提到他们follow了TREC的标准,来定义了这4个level。

值得注意的是,你提到的代码里的evaluator, MTEB实际跑的时候是没有调用到的 实际用到的evaluator应该下面是这行: https://github.com/embeddings-benchmark/mteb/blob/main/mteb/abstasks/AbsTaskRetrieval.py#L54 MTEB在retrieval的任务上采用了BEIR的evaluator,BEIR用的包则是pytrec_eval,看上去和T2Rank的标准是一致的。

我在翻越pytrec_eval的时候看到了这个issue,看上去1-5的rel系数也是支持的。 https://github.com/cvangysel/pytrec_eval/issues/4

在计算dcg 的时候,如果我理解的没错的话,计算增益的方式应该是 (2**rel - 1) / (log2(i+2)) 如果rel系数为0的话,增益也为0。 所以我得到的结论就是T2rank计算dcg也不需要额外的处理,计算应该没有问题。

Wenze7 commented 1 year ago

感谢!感谢!感谢!

Wenze7 commented 1 year ago

这个问题我之前也思考过。 我看T2 rank对4个level的定义如下: image

只有0是不相关的,1应该也算相关吧。T2rank提到他们follow了TREC的标准,来定义了这4个level。

值得注意的是,你提到的代码里的evaluator, MTEB实际跑的时候是没有调用到的 实际用到的evaluator应该下面是这行: https://github.com/embeddings-benchmark/mteb/blob/main/mteb/abstasks/AbsTaskRetrieval.py#L54 MTEB在retrieval的任务上采用了BEIR的evaluator,BEIR用的包则是pytrec_eval,看上去和T2Rank的标准是一致的。

我在翻越pytrec_eval的时候看到了这个issue,看上去1-5的rel系数也是支持的。 cvangysel/pytrec_eval#4

在计算dcg 的时候,如果我理解的没错的话,计算增益的方式应该是 (2**rel - 1) / (log2(i+2)) 如果rel系数为0的话,增益也为0。 所以我得到的结论就是T2rank计算dcg也不需要额外的处理,计算应该没有问题。

还想继续问下,在算MAP和MRR的时候还考虑这个相关性的档位吗?

hjq133 commented 1 year ago

实际的计算方式在pytrec_eval中应该有写,不过我c++实在不太好,没找到相关的代码。 不过根据我的理解,map@k是这样计算的:

  1. 对于每个query,计算precision@k: (number of relevant docs in top k) / k
  2. 对于所有的query的precision@k取均值。得到map@k

所以看上去计算map@k是不会在意top k内部是如何排位的,个人理解这种指标应该是给retrieval这种粗排任务用的。 而对于精排的话,看ndcg应该更合理一些。

wangyuxinwhy commented 1 year ago

MAP 的计算是通过 pytrec_eval ,把大于等于 1(relevance_level) 的当作相关,没有挡位的概念了,只有 0 和 大于等于 1。

class RelevanceEvaluator(_RelevanceEvaluator):
    def __init__(self, query_relevance, measures, relevance_level=1):
        measures = self._expand_nicknames(measures)
        measures = self._combine_measures(measures)
        super().__init__(query_relevance=query_relevance, measures=measures, relevance_level=relevance_level)

MRR 的计算在 beir 的代码中,也没有挡位的概念,只把分数大于 0 或者说大于等于 1 的看成是相关。和 MAP 的默认实现一致。

def mrr(qrels: Dict[str, Dict[str, int]], 
        results: Dict[str, Dict[str, float]], 
        k_values: List[int]) -> Tuple[Dict[str, float]]:

    MRR = {}

    for k in k_values:
        MRR[f"MRR@{k}"] = 0.0

    k_max, top_hits = max(k_values), {}
    logging.info("\n")

    for query_id, doc_scores in results.items():
        top_hits[query_id] = sorted(doc_scores.items(), key=lambda item: item[1], reverse=True)[0:k_max]   

    for query_id in top_hits:
        query_relevant_docs = set([doc_id for doc_id in qrels[query_id] if qrels[query_id][doc_id] > 0])    # 注意这里
        for k in k_values:
            for rank, hit in enumerate(top_hits[query_id][0:k]):
                if hit[0] in query_relevant_docs:
                    MRR[f"MRR@{k}"] += 1.0 / (rank + 1)
                    break