news 2026/9/23 13:25:16

PaddleNLP DuReader 评测指标模块深入解析:从答案抽取到 ROUGE-L/BLEU-4 评分

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
PaddleNLP DuReader 评测指标模块深入解析:从答案抽取到 ROUGE-L/BLEU-4 评分
  • 人工智能
  • 大模型
  • 预训练
  • 微调
  • LoRA
  • RLHF
  • 强化学习
  • 分布式训练

【免费下载链接】PaddleNLP

Easy-to-use and powerful LLM and SLM library with awesome model zoo.

项目地址:https://gitcode.com/gh_mirrors/pa/PaddleNLP
点击查看免费下载

导读

本文围绕 PaddleNLP 中paddlenlp.metrics.dureader评测模块展开,系统讲解中文阅读理解(MRC)评测的完整链路:如何把模型的起始/结束位置 logits 后处理为可读答案(compute_predictions/get_final_text)、如何对中文答案做归一化(normalize),以及如何用 ROUGE-L 与 BLEU-4 计算 DuReader 式开放答案的评测分数(dureader_evaluate)。读完本文,你将掌握该模块全部公开函数的参数语义、底层算法流程,以及它在 DuReader-yesno、DuReader-robust 等数据集与示例代码中的实际定位,可直接迁移到自己的抽取式问答评测流程中。

一、模块定位:DuReader 评测在 PaddleNLP 中的角色

1.1 文档页与实际源码

docs/zh/source/paddlenlp.metrics.dureader.rst是 Sphinx 自动文档(autodoc)的入口,它通过automodule指令将paddlenlp.metrics.dureader模块的所有公开成员渲染为 API 文档:

dureader ================================= .. automodule:: paddlenlp.metrics.dureader :members: :no-undoc-members: :show-inheritance:

:members:表示渲染全部公开函数,:no-undoc-members:表示跳过没有 docstring 的成员,:show-inheritance:显示继承关系。因此这份文档的“灵魂”其实是其背后的实现文件 paddlenlp/metrics/dureader.py,其中包含 4 个公开函数:compute_predictionsget_final_textnormalizedureader_evaluate,以及两个私有辅助函数_compute_softmax_get_best_indexes

1.2 DuReader 评测要解决什么问题

DuReader(百度阅读理解数据集)与 SQuAD 的一个关键差异在于答案形态:除了可从原文中抽取连续片段(Entity 型)之外,还包含需要模型自行生成的“描述型”答案(Description 型)和“是否型”答案(YesNo 型)。因此无法只用一个 F1/EM 类精确匹配指标,而需要:

  • 答案后处理:将模型输出的 start/end logits 转成最终文本;
  • 文本归一化:统一中英文标点(如全角逗号转半角),降低评测误差;
  • 模糊匹配指标:用 ROUGE-L(基于最长公共子序列)和 BLEU-4(n-gram 精确率)衡量生成答案与参考答案的相似度。

从源码结构看,paddlenlp/metrics/dureader.py的模块 docstring 也明确说明其源自 “Official evaluation script for SQuAD version 2.0”,并在其基础上加入了 DuReader 的中文评测特性。

二、答案后处理:compute_predictions 全流程剖析

2.1 函数签名与输入输出

def compute_predictions( all_examples, all_features, all_results, n_best_size, max_answer_length, do_lower_case, verbose, tokenizer ): """Write final predictions to the json file and log-odds of null if needed."""

见 paddlenlp/metrics/dureader.py#L29-L161。参数含义如下:

参数类型/说明
all_examples原始样例列表,每条含qas_idquestion_textquestion_typedoc_tokensorig_answer_text等字段
all_features特征列表,每条含example_indexunique_idtokenstoken_to_orig_maptoken_is_max_context
all_results模型推理结果列表,每条含unique_idstart_logitsend_logits
n_best_size每个问题保留的候选答案数量(默认常用 20)
max_answer_length允许的最大答案长度(token 数),超长候选被过滤
do_lower_case是否小写化(传给get_final_text的 tokenizer 使用)
verbose是否打印对齐失败等调试信息
tokenizer用于把预测文本投影回原文的 tokenizer 实例

函数返回一个二元组(preds_for_eval, preds_for_test)

  • preds_for_evalOrderedDictqas_id -> 最终答案文本,直接喂给dureader_evaluate打分;
  • preds_for_test:列表,每条包含question_idquestionquestion_typeanswersyesno_answers字段,用于按 DuReader 官方格式写出测试预测文件。

2.2 核心流程:五步得到最终答案

第一步:建立索引映射。collections.defaultdict构建example_index -> featuresunique_id -> result两个映射,实现从样例到特征再到模型输出的三级关联。

第二步:生成候选 span。对每个特征,用私有函数_get_best_indexes(dureader.py#L279-L288)分别取出 start/end logits 中得分最高的n_best_size个位置索引,然后双重遍历所有(start_index, end_index)组合。源码中有一组严格的合法性过滤条件:

if start_index >= len(feature.tokens): # 越界 continue if start_index not in feature.token_to_orig_map: # 无法映射回原文 continue if not feature.token_is_max_context.get(start_index, False): # 非最大上下文片段 continue if end_index < start_index: # 起始位置晚于结束位置 continue length = end_index - start_index + 1 if length > max_answer_length: # 超出最大答案长度 continue

候选按start_logit + end_logit之和降序排列(dureader.py#L91),这一步实际上等价于对位置联合概率做近似打分。

第三步:去 token 化(de-tokenize)。对每个候选 span,先把 WordPiece token 拼接回词级别文本,并去掉##前缀拼接符:

tok_text = tok_text.replace(" ##", "") tok_text = tok_text.replace("##", "")

然后调用get_final_text(tok_text, orig_text, tokenizer, verbose)把预测文本精确投影回原始文档文本(细节见下一节)。seen_predictions字典保证同一文本只保留一次,避免重复候选。

第四步:兜底与空答案处理。若因过滤过严导致一个候选都没有,会填充一个text="empty"、logits 为 0 的占位预测(dureader.py#L134-L135)。best_non_null_entry记录分数最高的非空候选,最终preds_for_eval[example.qas_id]即取这个非空最优答案。

第五步:输出组织。preds_for_test中每条记录都显式携带question_typeyesno_answers字段(默认空列表),这表明该输出格式是面向 DuReader 官方评测脚本的。

三、文本对齐:get_final_text 与 token 级到字符级的投影

3.1 为什么需要对齐

模型的预测是基于 token 化后的文本,而评测需要的是原始文档中的子串。源码注释给出了典型反例:预测文本是steve smith,而原文是Steve Smith's——直接返回orig_text会多出's,直接返回pred_text又丢失了大小写等信息。因此需要一个“半复杂”的对齐启发式。

3.2 对齐算法三步

get_final_text(dureader.py#L164-L253)的实现思路:

  1. 空格剥离与映射:私有函数_strip_spaces对文本去空格,同时记录“无空格字符下标 -> 原文本下标”的映射ns_to_s_map
  2. 长度一致性检查:分别对pred_textorig_text剥离空格后比较长度,若不等则说明启发式失败,直接返回orig_text(避免产生错误文本)。
  3. 字符级反投影:先在tok_text中用find定位pred_text的起止位置,再通过tok_ns_to_s_maporig_ns_to_s_map两级映射,把 token 层位置换算回原始文本的字符区间,最终切出orig_text[orig_start_position : orig_end_position + 1]

verbose=True时,对齐失败(找不到文本、长度不一致、无法映射起止位置)都会打印诊断信息,便于调试。

四、中文答案归一化:normalize

normalize(dureader.py#L291-L314)负责把答案字符串规整为“空格连接字符”的规范形式,其要点是全角标点转半角

norm_s = norm_s.replace(",", ",") norm_s = norm_s.replace("。", ".") norm_s = norm_s.replace("!", "!") norm_s = norm_s.replace("?", "?") norm_s = norm_s.replace(";", ";") norm_s = norm_s.replace("(", "(").replace(")", ")") norm_s = norm_s.replace("【", "[").replace("】", "]")

此外还会先剔除所有空白字符(len(c.strip()) != 0的过滤)再拼接。输入为空列表时原样返回。这一步对中文评测非常关键:同一答案在不同标注中可能使用全角或半角标点,归一化后可避免标点形态差异导致的分数波动。

五、评分核心:dureader_evaluate 与 ROUGE-L / BLEU-4

5.1 调用方式与输出

def dureader_evaluate(examples, preds): bleu_eval = BLEU(4) rouge_eval = RougeL() ... metrics = {"ROUGE-L": round(rouge_l * 100, 2), "BLEU-4": round(bleu4 * 100, 2)} print(json.dumps(metrics).encode("utf8"))

见 dureader.py#L317-L340。它遍历examples,按qas_id查表取preds中的预测答案;若某个 qid 缺失会打印Missing prediction for %s提示;参考答案取example.orig_answer_text(为空则跳过该样本)。预测与参考答案都经过normalize后,分别喂给 BLEU 和 RougeL 指标实例累加,最终输出形如{"ROUGE-L": 88.23, "BLEU-4": 76.41}的 JSON。

5.2 BLEU(4) 的计算原理

BLEU类位于 paddlenlp/metrics/bleu.py,继承自paddle.metric.Metric。其数学定义为:

BP = 1 (c > r) 或 e^(1-r/c) (c <= r) BLEU = BP * exp(Σ w_n * log p_n)

其中c为候选长度、r为参考长度。实现上,count_ngramget_match_size统计候选 n-gram 与参考 n-gram 的匹配数(多参考取每个 n-gram 的最大计数,见 bleu.py#L26-L41),accumulate中每个 gram 的精确率若为 0 会被替换为sys.float_info.min以避免log(0),最后乘上 brevity penalty(长度惩罚)得到 BLEU 分数。

dureader_evaluate中使用的是BLEU(4),即n_size=4、权重均匀分配(每个 1/4),对应评测指标 BLEU-4。

5.3 RougeL 的计算原理

RougeL类位于 paddlenlp/metrics/rouge.py,基于**最长公共子序列(LCS)**衡量句子级结构相似度:

  • 对每个参考答案计算prec = LCS(cand, ref) / len(cand)rec = LCS(cand, ref) / len(ref)
  • 取多参考中的prec_maxrec_max,用gamma(默认 1.2)加权调和:
F = ((1 + gamma^2) * prec_max * rec_max) / (rec_max + gamma^2 * prec_max)

lcs方法用动态规划矩阵(numpy.zeros)求最长公共子序列长度(rouge.py#L154-L176)。dureader_evaluate最后把rouge_l乘 100 并保留两位小数,与 DuReader 官方评测报告口径一致。

六、面向 DuReader 赛制加分的 Bonus 变体

paddlenlp.metrics.dureader模块本身不直接包含 Bonus 逻辑,但它所依赖的 BLEU / RougeL 指标在 bleu.py 与 rouge.py 中提供了 DuReader 赛制专用子类,可与本文模块配套使用:

  • BLEUForDuReader(bleu.py#L233-L276):在标准 BLEU 基础上增加add_yn_bonus(YesNo 型问题,n-gram 带上yn_label标签匹配)与add_entity_bonus(Entity 型问题,n-gram 带上"ENTITY"标签),通过alpha(YesNo 权重,默认 1.0)和beta(Entity 权重,默认 1.0)控制加分强度。
  • RougeLForDuReader(rouge.py#L233-L284):LCS 长度加上alpha * yn_bonus + beta * entity_bonus后再计算精确率与召回率;其中add_yn_bonus在模型判断与参考答案的 YesNo 标签一致时奖励 LCS 长度,add_entity_bonus统计参考答案实体在候选文本中的出现长度。

这两个变体与dureader_evaluate的“归一化 + ROUGE-L + BLEU-4”思路互补,共同构成 DuReader 评测的两套口径:一套面向通用开放答案评分,一套面向含 YesNo/Entity 标签的赛制评分。

七、在仓库中的实际落地:数据集与示例

7.1 关联数据集

PaddleNLP 仓库内置了多个 DuReader 系数据集加载器,与评测模块配套使用:

  • paddlenlp/datasets/dureader_yesno.py:YesNo 型,提供 train/dev/test 三个 split,答案形式为 yes/no 标签(源码中labels直接取自source["yesno_answer"]);
  • paddlenlp/datasets/dureader_robust.py:鲁棒性评测集;
  • paddlenlp/datasets/dureader_checklist.py:基于 CheckList 方法构建的能力测试集;
  • paddlenlp/datasets/dureader_qg.py:面向问题生成任务。

7.2 示例代码中的评测调用

在 slm/examples/machine_reading_comprehension/DuReader-robust/run_du.py 中可以看到完整的评测流程:模型在evaluate函数中收集所有 batch 的start_logitsend_logits,然后调用compute_prediction(raw_dataset, data_loader.dataset, (all_start_logits, all_end_logits), False, args.n_best_size, args.max_answer_length)得到预测答案并写入prediction.json,最后调用squad_evaluate输出评测分数(run_du.py#L56-L92)。

值得注意的是,paddlenlp/metrics/squad.py 提供了针对 SQuAD 与 DuReader-robust 的compute_prediction/squad_evaluate,而paddlenlp.metrics.dureader模块面向的是原版 DuReader 风格(携带question_typeyesno_answers字段、使用 ROUGE-L/BLEU-4 打分)的评测。二者一脉相承:先做 start/end logits 的后处理抽取,再做指标计算,只是指标口径与输出格式不同。docs/zh/metrics.md的指标速查表中也将两者并列列出。

八、实战:把 dureader 模块接入你的问答评测

结合上文源码分析,可以给出一个最小可用的评测接入范式(以该模块 API 为准):

from paddlenlp.metrics.dureader import compute_predictions, dureader_evaluate # 1. 模型推理得到 logits,按 unique_id 组织为 all_results # all_results 中的每条需包含 unique_id / start_logits / end_logits # 2. 后处理:从 logits 还原答案文本 preds_for_eval, preds_for_test = compute_predictions( all_examples=examples, # 原始样例 all_features=features, # token 化特征 all_results=all_results, # 模型输出 n_best_size=20, # 候选答案数 max_answer_length=30, # 最大答案长度 do_lower_case=False, # 中文场景一般不做小写化 verbose=False, # 需要排查对齐问题时置 True tokenizer=tokenizer, # 用于 get_final_text 反投影 ) # 3. 评测:输出 {"ROUGE-L": xx.xx, "BLEU-4": xx.xx} dureader_evaluate(examples=examples, preds=preds_for_eval)

使用要点:

  • n_best_sizemax_answer_length是精度-召回权衡的关键旋钮:候选越多召回越高但噪音越大;答案过长会被length > max_answer_length直接过滤;
  • tokenizer必须与训练时一致,因为get_final_text依赖 tokenizer 的basic_tokenizer.tokenize行为来做字符对齐;
  • 中文答案务必经过normalize,模块内部已自动完成全角转半角;
  • 若数据带 YesNo/Entity 标签且按赛制评分,可改用BLEUForDuReader/RougeLForDuReader,并调节alphabeta权重。

九、小结

paddlenlp.metrics.dureader虽然是一份由 docs/zh/source/paddlenlp.metrics.dureader.rst 自动渲染生成的 API 文档,但其背后的源码提供了完整的 DuReader 中文阅读理解评测链路:compute_predictions负责把 logits 后处理为答案,get_final_text负责 token 级到字符级的精确对齐,normalize负责中英文标点归一化,dureader_evaluate最终以 ROUGE-L 与 BLEU-4 双指标打分;配合BLEUForDuReader/RougeLForDuReader的赛制加分变体,可覆盖 YesNo、Entity、Description 三种问题形态。无论是复现 DuReader 评测,还是构建自定义的抽取式问答评测流水线,这个模块都是值得直接复用与研读的参考实现。

  • 人工智能
  • 大模型
  • 预训练
  • 微调
  • LoRA
  • RLHF
  • 强化学习
  • 分布式训练

【免费下载链接】PaddleNLP

Easy-to-use and powerful LLM and SLM library with awesome model zoo.

项目地址:https://gitcode.com/gh_mirrors/pa/PaddleNLP
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/23 13:22:50

CNG加气站设计与建设关键技术解析

1. CNG加气站行业背景与需求分析压缩天然气&#xff08;CNG&#xff09;作为清洁能源在交通领域的应用已有30余年历史。根据行业数据显示&#xff0c;全球CNG车辆保有量年均增长率保持在8%以上&#xff0c;这种增长直接带动了加气站建设需求的持续攀升。与传统加油站相比&#…

作者头像 李华
网站建设 2026/9/23 13:20:57

FPGA实现PCF8563的I2C驱动:寄存器级Verilog状态机详解

简介&#xff1a;此压缩包是一套面向FPGA学习者的I2C接口RTC实时时钟工程&#xff0c;基于Verilog实现PCF8563芯片的读写控制&#xff0c;配套Quartus 18.0完整工程文件&#xff0c;适用Cyclone IV E系列EP4CE10F17C8器件。包内共124个文件&#xff0c;涵盖rtc顶层模块、i2c_dr…

作者头像 李华
网站建设 2026/9/23 13:20:02

V免签支付系统实战:安卓监听实现免签约收款回调

简介&#xff1a;这是一款基于Thinkphp内核的V免签支付系统安卓监控端&#xff0c;面向需要为应用接入支付宝、微信免签约收款的开发者&#xff0c;省去与支付机构正式签约的流程&#xff0c;帮助商家实时掌握收款动态。压缩包约34.03MB&#xff0c;共297个文件&#xff0c;其中…

作者头像 李华
网站建设 2026/9/23 13:14:40

基于Java的宠物店猫咖管理系统后端设计源码:Spring Boot与MyBatis实战

简介&#xff1a;这份源码面向Java后端初学者与需要课程设计、毕业设计参考的开发者&#xff0c;提供一套宠物店猫咖管理系统的后端实现方案&#xff0c;可帮助理解业务系统从建模到落地的完整思路。压缩包共38个文件、约52KB&#xff0c;以19个Java源文件承载核心业务逻辑&…

作者头像 李华
网站建设 2026/9/23 13:14:30

纯Python视觉SLAM实战:从环境配置到后端优化

简介&#xff1a;这是一份面向视觉SLAM初学者与研究者的纯Python实战项目包&#xff0c;围绕同时定位与建图的核心流程展开&#xff0c;涵盖单目、双目视觉里程计与SLAM、轨迹评估、回环检测等模块&#xff0c;适合希望深入理解算法实现细节、动手复现并优化SLAM系统的学习者。…

作者头像 李华