2. 实现精确匹配与包含匹配评测

任务目标

实现一个评测脚本,对模型生成的回答分别进行精确匹配(Exact Match)和包含匹配(Contains Match)评分,并输出两种指标的结果对比,用于观察宽松匹配对得分的影响。

核心实现

def exact_match(reference: str, prediction: str) -> int:
    """精确匹配:归一化后完全一致得1分,否则0分"""
    return int(_normalize(reference) == _normalize(prediction))

def contains_match(reference: str, prediction: str) -> int:
    """包含匹配:归一化后参考答案是预测答案的子串得1分"""
    return int(_normalize(reference) in _normalize(prediction))

def _normalize(text: str) -> str:
    """轻量归一化:去首尾空白、统一小写、去除内部多余空格"""
    return " ".join(text.strip().lower().split())

def evaluate(predictions: list, references: list) -> dict:
    """对成对样本计算两种指标,返回汇总统计与逐样本结果"""
    if len(predictions) != len(references):
        raise ValueError("predictions 与 references 长度不一致")

    em_scores, cm_scores = [], []
    details = []

    for pred, ref in zip(predictions, references):
        em = exact_match(ref, pred)
        cm = contains_match(ref, pred)
        em_scores.append(em)
        cm_scores.append(cm)
        details.append({
            "prediction": pred,
            "reference": ref,
            "exact_match": em,
            "contains_match": cm,
        })

    return {
        "exact_match_accuracy": sum(em_scores) / len(em_scores),
        "contains_match_accuracy": sum(cm_scores) / len(cm_scores),
        "sample_count": len(predictions),
        "details": details,
    }

if __name__ == "__main__":
    # 最小可运行示例:3个样本
    preds = [
        "北京是中国的首都",
        "机器学习是人工智能的一个分支",
        "Transformer 架构由 Google 提出",
    ]
    refs = [
        "北京是中国的首都。",
        "机器学习是人工智能的分支",
        "Transformer架构由Google在2017年提出",
    ]

    result = evaluate(preds, refs)
    print(f"样本数: {result['sample_count']}")
    print(f"精确匹配准确率: {result['exact_match_accuracy']:.2%}")
    print(f"包含匹配准确率: {result['contains_match_accuracy']:.2%}")
    print("\n逐样本明细:")
    for i, d in enumerate(result["details"]):
        print(f"样本{i+1}: EM={d['exact_match']} CM={d['contains_match']} | "
              f"预测: {d['prediction'][:20]}... | 参考: {d['reference'][:20]}...")

运行方式

python evaluate_match.py

无需安装第三方库,Python 3.6+ 即可直接运行。

验证方法与预期输出

运行脚本后观察控制台输出,满足以下条件即判定通过:

  • 程序正常结束,无异常报错;
  • 输出包含两种指标的准确率,且包含匹配准确率 ≥ 精确匹配准确率;
  • 逐样本明细中,每个样本的 EM 和 CM 值符合定义(EM=1 时 CM 必为 1,反之不一定)。

针对示例数据,预期输出为:

样本数: 3
精确匹配准确率: 33.33%
包含匹配准确率: 100.00%

逐样本明细:
样本1: EM=0 CM=1 | 预测: 北京是中国的首都... | 参考: 北京是中国的首都。...
样本2: EM=0 CM=1 | 预测: 机器学习是人工智能的一个分支... | 参考: 机器学习是人工智能的分支...
样本3: EM=1 CM=1 | 预测: Transformer 架构由 Google 提出... | 参考: Transformer架构由Google在2017年提出...

其中样本1因标点差异精确匹配失败但包含匹配成功;样本2因“一个”多余词导致精确匹配失败;样本3因空格和年份补充导致精确匹配失败。三种情况直观展示了两种指标的差异。

精确匹配 vs 包含匹配:判定逻辑对比 样本1:标点差异 参考: "北京是中国的首都。" 预测: "北京是中国的首都" EM = 0 CM = 1 样本2:多余词 参考: "机器学习是人工智能的分支" 预测: "机器学习是人工智能的一个分支" EM = 0 CM = 1 样本3:空格与补充信息 参考: "Transformer架构由Google在2017年提出" 预测: "Transformer 架构由 Google 提出" EM = 0 CM = 1 归一化后完全一致 → EM=1;参考答案是预测的子串 → CM=1