3.5 上下文管理的量化评估方法
3.5.1 引言:为什么需要量化评估
3.5.2 相关性评分指标
基础相关性指标
from typing import List, Tuple, Set
import numpy as np
class RelevanceScorer:
"""相关性评分工具"""
@staticmethod
def mean_reciprocal_rank(
retrieved_items: List[bool], # True表示相关
threshold_position: int = 10
) -> float:
"""
平均倒数排名 (Mean Reciprocal Rank, MRR)
衡量第一个相关项目出现的位置。
越靠前,分数越高。
公式: MRR = (1/position of first relevant item)
示例:
- [True, False, False] -> 1/1 = 1.0 (最优)
- [False, True, False] -> 1/2 = 0.5
- [False, False, False] -> 0.0 (无相关项)
"""
for i, is_relevant in enumerate(retrieved_items[:threshold_position]):
if is_relevant:
return 1.0 / (i + 1)
return 0.0
@staticmethod
def recall_at_k(
retrieved_items: List[bool],
relevant_item_count: int,
k: int = 10
) -> float:
"""
召回率 (Recall@k)
在前k个结果中,找到了多少个相关项。
公式: Recall@k = (前k个结果中的相关数) / 总相关数
示例:
- 总共10个相关项,前20个结果中找到8个 -> 8/10 = 0.8
- 总共5个相关项,前10个结果中找到3个 -> 3/5 = 0.6
"""
if relevant_item_count == 0:
return 0.0
relevant_in_top_k = sum(retrieved_items[:k])
return relevant_in_top_k / relevant_item_count
@staticmethod
def precision_at_k(
retrieved_items: List[bool],
k: int = 10
) -> float:
"""
精确度 (Precision@k)
前k个结果中有多少比例是相关的。
公式: Precision@k = (前k个中的相关数) / k
示例:
- 前10个结果中有8个相关 -> 8/10 = 0.8
- 前5个结果中有1个相关 -> 1/5 = 0.2
"""
if k == 0:
return 0.0
relevant_in_top_k = sum(retrieved_items[:k])
return relevant_in_top_k / k
@staticmethod
def f1_score(
retrieved_items: List[bool],
relevant_item_count: int,
k: int = 10
) -> float:
"""
F1分数 (F1 Score)
Precision和Recall的调和平均。
在精确度和召回率之间找到平衡。
公式: F1 = 2 * (Precision * Recall) / (Precision + Recall)
"""
precision = RelevanceScorer.precision_at_k(retrieved_items, k)
recall = RelevanceScorer.recall_at_k(retrieved_items, relevant_item_count, k)
if precision + recall == 0:
return 0.0
return 2 * (precision * recall) / (precision + recall)
@staticmethod
def ndcg(
relevance_scores: List[float],
k: int = 10,
ideal_dcg: float = None
) -> float:
"""
归一化折损累计增益 (Normalized Discounted Cumulative Gain, NDCG)
考虑相关性的"程度"(不只是相关/不相关)。
排名靠前的相关项贡献更大。
公式:
DCG@k = sum(relevance[i] / log2(i+2)) for i in 0..k-1
NDCG@k = DCG@k / IDCG@k
其中IDCG是完美排序下的DCG。
示例:
relevance_scores = [0.9, 0.7, 0.0, 0.5, 0.8]
DCG@5 = 0.9/1 + 0.7/log2(3) + 0/log2(4) + 0.5/log2(5) + 0.8/log2(6)
"""
if not relevance_scores or k == 0:
return 0.0
# 计算实际的DCG
dcg = 0.0
for i, score in enumerate(relevance_scores[:k]):
# 注意:log2(i+2)表示从位置1开始计数
dcg += score / np.log2(i + 2)
# 计算理想的DCG(如果没有给出,则用排序后的最优值)
if ideal_dcg is None:
sorted_scores = sorted(relevance_scores, reverse=True)
ideal_dcg = 0.0
for i, score in enumerate(sorted_scores[:k]):
ideal_dcg += score / np.log2(i + 2)
if ideal_dcg == 0:
return 0.0
return dcg / ideal_dcg
# 使用示例
if __name__ == "__main__":
# 场景:搜索"Python教程",返回了10个结果
# 其中标注的相关性:
retrieved = [
True, # 位置1: 很相关
False, # 位置2: 不相关
True, # 位置3: 相关
False, # 位置4: 不相关
False, # 位置5: 不相关
True, # 位置6: 相关
False, # 位置7: 不相关
False, # 位置8: 不相关
True, # 位置9: 相关
False, # 位置10: 不相关
]
relevant_count = sum(retrieved) # 4个相关项
scorer = RelevanceScorer()
mrr = scorer.mean_reciprocal_rank(retrieved)
recall = scorer.recall_at_k(retrieved, relevant_count, k=10)
precision = scorer.precision_at_k(retrieved, k=10)
f1 = scorer.f1_score(retrieved, relevant_count, k=10)
print(f"MRR: {mrr:.2f}") # 应该是1.0(第1个位置)
print(f"Recall@10: {recall:.2f}") # 应该是1.0(找到全部4个)
print(f"Precision@10: {precision:.2f}") # 应该是0.4(4/10)
print(f"F1: {f1:.2f}")分级相关性指标
3.5.3 检索质量度量
多角度评估检索系统
3.5.4 上下文效率指标
3.5.5 A/B测试框架在上下文优化中的应用
最后更新于
