RAG 评估 - 检索指标

检索评估的主要目标是评估上下文相关性,即检索到的文档与用户查询的匹配程度。它确保提供给生成组件的上下文是相关和准确的,

  1. 命中率 hit_rate:检索文档有多少是目标文档
  2. 平均倒数排名 mrr:MRR 计算的是最相关文档的倒数排名的平均值。如果正确答案的排名是高的(即排名接近第一位),则倒数值会大。MRR 值的范围是 0 到 1,值越高表示检索系统性能越好
  3. precision:被检索出来的目标文档数量 / 检索数量,表示检索器的精准率
  4. recall:被检索出来的目标文档数量 / 目标文档数量,表示检索器找到目标文档的完整度
  5. ap: 首先计算每个查询的平均精确率,然后对所有查询的平均精确率进行平均。这里的 “精确率” 是指在每个排名阶段检索到的相关文档数量占到目前为止所有检索到的文档数量的比例。MAP 考虑了所有相关文档,并且对检索结果的排序非常敏感,值越高,表示检索系统的排名性能越好
  6. 归一化折现累积增益 ndcg:NDCG 倾向于赋予排在前面的相关文档更高的权重,是一种位置敏感的度量方法。NDCG 先计算一个未经归一化的折扣累积增益(Discounted Cumulative Gain, DCG),然后用这个值除以一个理想状态(即最佳排名顺序)下的 DCG,从而得到归一化的值。NDCG 的值范围在 0 到 1 之间,越接近 1 表示检索性能越佳,特别是在前面几个结果的质量方面。
  7. cohere_rerank_relevancy:不使用 expected_ids、expected_texts,直接 query+retrieved_texts 输入到 llm 回答问题,然后 llm 给定答案打分,然后取打分的均值或者最大值返回。

检索评估指标可以归纳为以下模式

指标示例
expected_ids[1,2,3]
retrieved_ids[1,3,4]
expected_texts[‘ab’,‘shs’]
retrieved_texts[‘ab’,‘wjw’]

通过以上 4 个列表,计算一个查询的评估值

命中率 hit_rate

方式 1:expected_ids 被命中的数量 /expected_ids 数量
方式 2: 只要 expected_ids 被命中,返回 1

1
2
3
4
5
6
7
8
9
if self.use_granular_hit_rate:
# Granular HitRate calculation: Calculate all hits and divide by the number of expected docs
expected_set = set(expected_ids)
hits = sum(1 for doc_id in retrieved_ids if doc_id in expected_set)
score = hits / len(expected_ids) if expected_ids else 0.0
else:
# Default HitRate calculation: Check if there is a single hit
is_hit = any(id in expected_ids for id in retrieved_ids)
score = 1.0 if is_hit else 0.0

平均倒数排名 mrr

考虑命中率的同时,考虑 rank 顺序,命中越靠前,分数越高

方式 1:如果 retrieved_ids 的第 i 位在 expected_ids 中,分数 + 1/i,否则 + 0,最后取分数平均值
方式 2: 取 retrieved_ids 最先命中 expected_ids 的位置,假设是 i,直接取 1/i 作为最终结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
if self.use_granular_mrr:
# Granular MRR calculation: All relevant retrieved docs have their reciprocal ranks summed and averaged
expected_set = set(expected_ids)
reciprocal_rank_sum = 0.0
relevant_docs_count = 0
for index, doc_id in enumerate(retrieved_ids):
if doc_id in expected_set:
relevant_docs_count += 1
reciprocal_rank_sum += 1.0 / (index + 1)
mrr_score = (
reciprocal_rank_sum / relevant_docs_count
if relevant_docs_count > 0
else 0.0
)
else:
# Default MRR calculation: Reciprocal rank of the first relevant document retrieved
for i, id in enumerate(retrieved_ids):
if id in expected_ids:
return RetrievalMetricResult(score=1.0 / (i + 1))
mrr_score = 0.0

precision

比较 retrieved_ids、expected_ids,计算 tp、fp、fn,然后计算出 precision

1
2
3
retrieved_set = set(retrieved_ids)
expected_set = set(expected_ids)
precision = len(retrieved_set & expected_set) / len(retrieved_set)

recall

比较 retrieved_ids、expected_ids,计算 tp、fp、fn,然后计算出 precision

1
2
3
retrieved_set = set(retrieved_ids)
expected_set = set(expected_ids)
recall = len(retrieved_set & expected_set) / len(expected_set)

ap

利用 rank 位置,通过积分求取 precision

1
2
3
4
5
6
7
8
9
expected_set = set(expected_ids)

relevant_count, total_precision = 0, 0.0
for i, retrieved_id in enumerate(retrieved_ids, start=1):
if retrieved_id in expected_set:
relevant_count += 1
total_precision += relevant_count / i

average_precision = total_precision / len(expected_set)

归一化折现累积增益 ndcg

  • 首先提取 retrieved_ids 命中 expected_ids 的位置,假设为 i,然后累加 1 / math.log2(i + 1)
  • 使用 retrieved_ids、expected_ids 的最小长度再计算一次累加值
  • 最终结果:第一个累加值 / 第二个累加值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
def discounted_gain(*, rel: float, i: int, mode: DiscountedGainMode) -> float:
# Avoid unnecessary calculations. Note that `False == 0` and `True == 1`.
if rel == 0:
return 0
if rel == 1:
return 1 / math.log2(i + 1)

if mode == "linear":
return rel / math.log2(i + 1)
elif mode == "exponential":
return (2**rel - 1) / math.log2(i + 1)
else:
assert_never(mode)

expected_set = set(expected_ids)

# Calculate DCG
dcg = sum(
discounted_gain(rel=docid in expected_set, i=i, mode=mode) # model="linear"
for i, docid in enumerate(retrieved_ids, start=1)
)

# Calculate IDCG using min(len(retrieved_ids), len(expected_ids))
# Since we can't achieve better than perfect ranking of all relevant docs
ideal_length = min(len(retrieved_ids), len(expected_ids))
idcg = sum(
discounted_gain(rel=True, i=i, mode=mode)
for i in range(1, ideal_length + 1)
)

# Handle edge case where there are no relevant documents
if idcg == 0:
return RetrievalMetricResult(score=0.0)

ndcg_score = dcg / idcg

cohere_rerank_relevancy

不使用 expected_ids、expected_texts,直接 query+retrieved_texts 输入到 llm 回答问题,然后 llm 给定答案打分,然后取打分的均值或者最大值返回