多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

PaddleNLP Distinct 文本多样性指标:从原理到源码级实践指南

PaddleNLP Distinct 文本多样性指标:从原理到源码级实践指南 人工智能大模型预训练微调LoRARLHF强化学习分布式训练【免费下载链接】PaddleNLPEasy-to-use and powerful LLM and SLM library with awesome model zoo.项目地址https://gitcode.com/gh_mirrors/pa/PaddleNLP点击查看免费下载本文聚焦 PaddleNLP 中用于评估生成文本多样性的Distinct指标先讲清去重 n-gram 占比的核心算法原理再逐段剖析 paddlenlp/metrics/distinct.py 的类实现与状态维护机制最后通过完整可运行的示例演示普通评估对象与paddle.metric.Metric两种接入方式。读完你既能独立计算对话、生成任务的多样性分数也能将Distinct无缝接入 PaddleNLP 训练与评估流程。一、为什么需要 Distinct衡量生成文本的多样性在文本生成对话、摘要、机器翻译任务中模型容易陷入安全但重复的输出——例如反复使用高频词汇和固定搭配。传统的 BLEU、ROUGE 等指标衡量的是与参考答案的重合度无法反映生成内容本身的多样性。Distinct正是为此设计的指标它通过统计生成文本中不同的 n-gram 数量占比来量化文本多样性不同 n-gram 数量越多说明文本越丰富、越不重复。从源码 docstring 可以看到该算法依据 Li 等人发表于 2016 年的论文A Diversity-Promoting Objective Function for Neural Conversation ModelsarXiv:1510.03055该工作首次将 Distinct-1 / Distinct-2 作为对话系统多样性评估的标准化指标引入此后被广泛沿用。Distinct定义于 paddlenlp/metrics/distinct.py对外通过 paddlenlp/metrics/init.py 的from .distinct import Distinct导出因此可直接from paddlenlp.metrics import Distinct使用。二、算法原理去重 n-gram 占比假设生成的候选句子以 token 列表表示Distinct的计算过程分为两步滑窗切分 n-gram从句子第一个 token 开始以步长 1 滑动窗口切出长度为n_size的连续 token 序列相邻 token 之间以空格连接成一个整体串计算去重占比用不同 n-gram 的种类数除以n-gram 总数得到最终分数。用源码中add_inst的逻辑表达即为score len(diff_ngram_set) / total_ngram_count分数取值范围为(0, 1]。分数越高文本中重复的 n-gram 越少多样性越好。默认n_size2即统计不同的 bigram 占比对应论文中的 Distinct-2当n_size1时即 Distinct-1不同的 unigram / 单词占比。以[The, cat, The, cat, on, the, mat]为例手工推导 Distinct-2 的计算过程如下滑窗位置bigram 内容0–1The cat1–2cat The2–3The cat重复3–4cat on4–5on the5–6the mat共 6 个 bigram去重后仅剩 5 个不同 bigramThe cat 出现了两次因此 Distinct 分数为5 / 6 ≈ 0.8333。这一结果与仓库中的单元测试 tests/metrics/test_distinct.py 断言的0.8333333333333334完全一致可作为你验证环境正确性的基准用例。三、源码级解析Distinct 类的设计与实现Distinct继承自paddle.metric.Metric位于 paddlenlp/metrics/distinct.py整个类体量很小但状态维护、增量更新、指标聚合的逻辑非常清晰适合作为自定义评估指标的参考模板。3.1 构造与内部状态def __init__(self, n_size2, trans_funcNone, namedistinct): super(Distinct, self).__init__() self._name name self.diff_ngram set() # 去重后的 n-gram 集合 self.count 0.0 # n-gram 总计数 self.n_size n_size self.trans_func trans_func类内部维护两个核心状态self.diff_ngram一个 Pythonset保存所有出现过的 n-gram 串利用集合天然去重的特性无需手动判重self.count累计的 n-gram 总数浮点数。通过集合大小 / 总计数的比值即可在任意时刻给出多样性分数且支持多条句子增量累加——这正是它能在评估循环中逐 batch 更新状态的原理。3.2 增量更新add_instdef add_inst(self, cand): for i in range(0, len(cand) - self.n_size 1): ngram .join(cand[i : (i self.n_size)]) self.count 1 self.diff_ngram.add(ngram)add_inst接收一条已经分词好的候选 token 列表cand用滑窗取出长度为n_size的连续 token以空格 .join拼成字符串作为 n-gram 的唯一标识这保证了不同 token 组合不会被误判为同一个 n-gram每取一个 n-gram 就把count加 1同时将其加入去重集合。滑窗循环边界为len(cand) - n_size 1保证句子长度不足n_size时不会产出任何 n-gram此时count保持为 0。3.3 批量接入updatedef update(self, output, *args): if isinstance(output, paddle.Tensor): output output.numpy() assert self.trans_func is not None, ( The update method requires user to provide trans_func when initializing Distinct. ) cand_list self.trans_func(output) for cand in cand_list: self.add_inst(cand)update是paddle.metric.Metric标准接口负责把模型输出喂给指标先将paddle.Tensor转为numpy.ndarray方便在纯 Python 层做后处理再调用用户提供的trans_func把网络输出转换为字符串列表的列表即多条已分词候选句最后逐条调用add_inst更新状态。值得注意的是update内部有硬性断言以 Metric 方式使用时必须提供trans_func否则直接抛异常。3.4 汇总、重置与命名accumulate()返回len(self.diff_ngram) / self.count即最终 Distinct 分数score()与accumulate()完全等价便于在普通对象场景下取分数reset()将diff_ngram重置为空集合、count归零用于开启新一轮评估name()返回指标名默认为distinct配合 Trainer / 日志系统展示指标名称。需要留意一个边界若从未调用add_inst或所有句子长度都小于n_sizecount为 0此时accumulate()会触发除零异常实际使用时应保证输入句子长度不小于n_size。四、参数详解Distinct(n_size2, trans_funcNone, namedistinct)的参数含义如下参数类型默认值说明n_sizeint2计算几元语法1 为 unigram、2 为 bigram越大越关注短语级多样性trans_funccallableNone将模型输出numpy 数组转换为字符串列表的列表的函数以paddle.metric.Metric方式使用时必填输入为 numpy 数组namestrdistinct指标实例名称出现在日志与评估报告中关于trans_func的约定它的输入是update传入的numpy.ndarray模型 logits输出必须是list[list[str]]形式——外层列表对应 batch 中的每条样本内层列表是这条样本已分词的 token 序列。trans_func的典型实现是对 logits 取argmax得到 token id再借助 Tokenizer 的convert_ids_to_tokens与convert_tokens_to_string还原出自然语言 token 列表。五、两种使用方式从快速验证到训练评估文档给出了两种接入方式均已在 paddlenlp/metrics/distinct.py 的 docstring 中提供可运行示例。5.1 方式一作为普通评估对象适合离线评测直接构造Distinct()逐条调用add_inst喂入已分词候选句最后调用score()取分数。from paddlenlp.metrics import Distinct distinct Distinct() cand [The, cat, The, cat, on, the, mat] # update the states distinct.add_inst(cand) print(distinct.score()) # 0.8333333333333334多条句子可以连续调用add_inst累加状态跨句合并统计去重 n-gram 占比评估完一轮后调用reset()清空状态即可开始下一轮。5.2 方式二作为 paddle.metric.Metric 实例适合嵌入训练 / 评估流程通过partial把 Tokenizer 绑定进trans_func模型输出的 logits 通过update批量喂入最后用accumulate()取指标值。需要先安装并准备bert-base-uncased分词器BertTokenizer.from_pretrained会从模型中心下载对应词表。import numpy as np from functools import partial import paddle from paddlenlp.transformers import BertTokenizer from paddlenlp.metrics import Distinct def trans_func(logits, tokenizer): Transform the network output logits to string list. # [batch_size, seq_len] token_ids np.argmax(logits, axis-1).tolist() cand_list [] for ids in token_ids: tokens tokenizer.convert_ids_to_tokens(ids) strings tokenizer.convert_tokens_to_string(tokens) cand_list.append(strings.split()) return cand_list paddle.seed(2021) tokenizer BertTokenizer.from_pretrained(bert-base-uncased) distinct Distinct(trans_funcpartial(trans_func, tokenizertokenizer)) batch_size, seq_len, vocab_size 4, 16, tokenizer.vocab_size logits paddle.rand([batch_size, seq_len, vocab_size]) distinct.update(logits.numpy()) print(distinct.accumulate()) # 1.0该示例在完全随机的 logits 上得到 1.0 的满分因为随机概率分布下 16 个位置的 token id 几乎不会出现重复的 bigram。在实际训练中将distinct.update放进评估循环、按 epoch 结束时reset()即可持续观测生成多样性。六、测试用例指标正确性的回归保障仓库在 tests/metrics/test_distinct.py 中提供了针对该指标的单元测试import unittest from paddlenlp.metrics import Distinct class TestDistinct(unittest.TestCase): def test_metrics(self): distinct Distinct() cand [The, cat, The, cat, on, the, mat] distinct.add_inst(cand) self.assertEqual(distinct.score(), 0.8333333333333334)该用例直接断言了第二节手工推导的 5/6 结果从普通对象调用 分数计算两个层面锁定了算法实现的正确性任何对滑窗边界或去重逻辑的误改都会被该用例捕获。这也是验证你自己环境中 PaddleNLP 安装正确性的最快途径运行python -m unittest tests.metrics.test_distinct或直接执行该文件即可。七、在 PaddleNLP 指标体系中的定位Distinct是 paddlenlp/metrics/init.py 统一导出的一组评估指标之一与它并列的还有BLEU机器翻译质量、Rouge1/Rouge2/RougeL摘要质量、Perplexity语言模型困惑度、MRR检索排序以及ChunkEvaluator、SpanEvaluator序列标注等。它们的共同点是均继承自paddle.metric.Metric遵循update → reset → accumulate/score的统一生命周期因此可以在同一套 Trainer 评估框架中自由组合。相比BLEU等需要参考答案的重合度指标Distinct是无参考的生成质量指标衡量的是生成内容本身的丰富度两者互补使用能更全面地刻画生成模型的表现。八、实践建议与注意事项n_size 的选择对话 / 开放生成通常报告 Distinct-1 与 Distinct-2 两个值分别反映词汇级与短语级多样性n_size越大对长距离重复的敏感度越高但对短句会因 n-gram 样本过少而失真。保证句子长度句子 token 数必须不小于n_size否则该句不产生任何 n-gram且全空输入会导致accumulate除零。分词一致性trans_func输出的 token 需与训练时 Tokenizer 的分词方式保持一致因为 n-gram 以空格拼接的 token 串为标识不同的分词粒度会直接影响去重结果。与 BLEU 的差异BLEU是带参考的匹配精度Distinct是无参考的多样性度量二者不能互相替代评估生成任务时应同时报告。通过本文你已掌握Distinct的算法本质、源码实现细节、参数语义与两种接入方式可以直接将其用于对话模型、生成模型的多样性质控或作为自定义 PaddleNLP 评估指标的实现范本。赞分享人工智能大模型预训练微调LoRARLHF强化学习分布式训练【免费下载链接】PaddleNLPEasy-to-use and powerful LLM and SLM library with awesome model zoo.项目地址https://gitcode.com/gh_mirrors/pa/PaddleNLP点击查看免费下载相关推荐PaddleNLP Funnel Transformer 模型实战指南从 API 参考到源码级原理解析PaddleNLP Funnel Transformer 模型实战指南从 API 参考到源码级原理解析 导读 Funnel Transformer 是 Pad人工智能大模型预训练微调LoRARLHF强化学习分布式训练模型推理服务推理引擎模型量化模型压缩本地部署NLPPaddleNLP 文本生成评测指标 RougeL / RougeN 源码级解析从 LCS 算法到 DuReader 实战PaddleNLP 文本生成评测指标 RougeL / RougeN 源码级解析从 LCS 算法到 DuReader 实战 RougeRecall Orie人工智能大模型预训练微调LoRARLHF强化学习分布式训练模型推理服务推理引擎模型量化模型压缩本地部署NLP变分自编码器VAE实战指南从原理到多样化样本生成变分自编码器VAE实战指南从原理到多样化样本生成 变分自编码器VAE是深度学习领域中强大的生成模型能够学习数据分布并生成多样化的新样本。作为机器学习ye文档教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表