欺诈文本分类检测(十六):支持分类原因评测改造

1. 引言

经过前文对数据的校正与增强后,我们的预期生成结果中不再仅仅是分类标签,还多了欺诈者和分类原因。这样之前模型评测和批量评测两篇文章所封装的evaluate.py脚本就不再满足,需要对脚本进行改造,以支持新输出内容的评测。

新的预期结果中共包含三个信息,由于三个信息的特点不同,需要为每个字段制定不同的评测方式:

  • is_fraud: 属于二分类,继续采用精确率和召回率作为评测指标。
  • fraud_speaker: 属于多分类,多分类没有混淆矩阵,可以使用sklearn包提供的accuracy_score计算准确率作为评测指标。
  • reason: 属于分文生成,由于没有准确值,可以通过rouge_score计算文本串的相似度作为评测指标。

2. rouge指标调研

ROUGE(Recall-Oriented Understudy for Gisting Evaluation)指标是用于评估文本摘要质量的一种常用指标。它通过比较生成的摘要与参考摘要之间的重叠词语或短语来衡量它们之间的相似度。Rouge包含多个子指标:

  • ROUGE-1: 评估生成文本(hypothesis)与参考文本(reference)之间的单词级别的重叠;
  • ROUGE-2: 评估生成文本和参考文本相邻两个词(或字)的重叠情况,它关注的是两个文本中二元组(bigrams)的匹配,以此类推可以推广到ROUGE-N;
  • ROUGE-L:与前两者不同的是,此指标关注的是生成文本与参考文本之间的最长公共子序列(Longest Common Subsequence, LCS),通过最长公共子序列来衡量两个文本之间的相似性,最长公共子序列是指在不改变顺序的情况下,两个序列中都存在的元素的最长序列。

下面来实测一下。

from rouge import Rouge

def rouge_score(predictions, realitys):
    rouge = Rouge()
    return rouge.get_scores(predictions, realitys, avg=True)

先用英文测试。

predictions = "you are a programer."
realitys = "you are the programer."
scores = rouge_score(predictions, realitys)
scores
    {'rouge-1': {'r': 0.75, 'p': 0.75, 'f': 0.749999995},
     'rouge-2': {'r': 0.3333333333333333,
      'p': 0.3333333333333333,
      'f': 0.3333333283333334},
     'rouge-l': {'r': 0.75, 'p': 0.75, 'f': 0.749999995}}
  • 用rouge-1衡量时,predictions和realitys中有[you, are, programer]三个单词相同,生成文本和参考文本都是共4个单词,所以精确率和召回率都是75%;
  • 用rouge-2衡量时,predictions和realitys中只有you are一个二元组相同,生成文本和参考文本都有4个单词(即3个二元组),所以精确率和召回率都是33%;
  • 用rouge-l衡量时,predictions和realitys中的最长公共子序列是['you', 'are', 'programer'],生成文本和参考文本的总长度都是4,所以精确率和召回率都是75%;

下面再用中文测试。

predictions = "你是一个程序员。"
realitys = "你就是那个程序员。"
scores = rouge_score(predictions, realitys)
scores
    {'rouge-1': {'r': 0.0, 'p': 0.0, 'f': 0.0},
     'rouge-2': {'r': 0.0, 'p': 0.0, 'f': 0.0},
     'rouge-l': {'r': 0.0, 'p': 0.0, 'f': 0.0}}

所有指标的精确率、召回率、f1-score都是0。

原因在于:rouge默认只支持按英文标准的空格符号来进行分词,由于上面的中文文本中没有空格,所以被当成一个词来进行重叠度计算,没有任何重叠所以输出是0。

需要引入jieba中文分词库,并封装一个tokenize方法专门用于对文本分词。

import jieba

def tokenize(text):
    return ' '.join(list(jieba.cut(text, cut_all=False)))

上面的tokenize方法中,先使用jieba.cut方法进行分词,再将分词后的序列用空格连接,转换成与英文文本相同的分隔符,以便rouge库能自动分词。

测试分词效果。

tokenized_predictions = tokenize(predictions)
tokenized_realitys = tokenize(realitys)
tokenized_predictions, tokenized_realitys

    ('你 是 一个 程序员 .', '你 就是 那个 程序员 .')

注:jieba分词的详细介绍和用法,请参考:jieba分词初探

对分词后的中文序列计算rouge分数。

scores = rouge_score(tokenized_predictions, tokenized_realitys)
scores
{'rouge-1': {'r': 0.5, 'p': 0.5, 'f': 0.4999999950000001},
 'rouge-2': {'r': 0.0, 'p': 0.0, 'f': 0.0},
 'rouge-l': {'r': 0.5, 'p': 0.5, 'f': 0.4999999950000001}}

4个词中有, 程序员两个词相同,单词重叠率为50%,所以rouge-1的分数为50%。

上面的例子中能够看出,rouge-1和rouge-l的两个指标更贴合我们的场景,但这两个指标有何不同呢?我们换一个例子来看。

tokenized_predictions = tokenize("他首先是一名老师,然后是一名程序员。")
tokenized_realitys = tokenize("他首先是一名程序员,然后是一名老师。")
print(tokenized_predictions, "\n", tokenized_realitys)
scores = rouge_score(tokenized_predictions, tokenized_realitys)
scores
    他 首先 是 一名 老师 , 然后 是 一名 程序员 。 
     他 首先 是 一名 程序员 , 然后 是 一名 老师 。

    {'rouge-1': {'r': 1.0, 'p': 1.0, 'f': 0.999999995},
     'rouge-2': {'r': 0.7777777777777778,
      'p': 0.7777777777777778,
      'f': 0.7777777727777778},
     'rouge-l': {'r': 0.7777777777777778,
      'p': 0.7777777777777778,
      'f': 0.7777777727777778}}

在上面这个例子中,预测序列和参考序列的单词完全相同,只是顺序不同,rouge-1不考虑单词顺序,最长子序列['他', '首先', '是', '一名', '老师', '然后', '是', '一名', '程序员']囊括了所有的词, 所以得出的精确率和召回率均是100%;而rouge-L关注了单词的顺序,所以最长子序列['他', '首先', '是', '一名', '然后', '是', '一名']只有7个词,而参考序列和预测序列的长度是9个词,所以精确率和召回率为77.7%。

由此可见,如果不考虑序列中词的顺序,则选择rouge-1,反之,如果考虑序列中词的顺序,则选择rouge-L。实际场景中,文本的顺序一般都会影响文本所表达的含义,所以为了尽可能准确,我们的场景选择rouge-L。

3. 脚本改造

首先,引入原始的评测脚本evaluate.py

%run evaluate.py

封装用于计算中文rouge分数的方法rouge_zh_score, 它接受两个文本串集合pred_dataset和real_dataset作为参数,返回集合中两两序列相似度的平均分数。

def rouge_zh_score(pred_dataset, real_dataset):
    # 首先,去掉两个集合中的空串,
    filtered_pairs = [(p, r) for p, r in zip(pred_dataset, real_dataset) if p.strip() != "" and r.strip() != ""]  
    filtered_predictions, filtered_realitys = zip(*filtered_pairs) if filtered_pairs else ([], [])  
    # 进行中文分词,因为Rouge分数的计算依赖于分词后的单词序列。
    predictions = [tokenize(text) for text in filtered_predictions]
    realitys = [tokenize(text) for text in filtered_realitys]
    # 计算两个序列的rouge分数
    score = rouge_score(predictions, realitys)
    return score['rouge-l']

注1:两个集合是按下标两两配对的,当出现空串时需要在两个集合中同时去掉相应位置的元素,目的是保持两个集合的配对关系不变。zip方法的作用是将两个集合两两配对,空串去完后还需要用*操作符将配对的集合解包还原为两个集合。

注2:需要去掉空串的原因在于,在计算 ROUGE 分数时,当任意一方字符串为空,将会导致“Hypothesis is empty.”的错误。

测试下rouge_zh_score方法的效果。

predictions = ["你是一个程序员。", "他首先是一名老师,然后是一名程序员。"]
realitys = ["你就是那个程序员。", "他首先是一名程序员,然后是一名老师。"]
rouge_zh_score(predictions, realitys)
    {'r': 0.6888888888888889, 'p': 0.6888888888888889, 'f': 0.6888888838888889}

通过上面测试可以发现,rouge.get_scores方法既支持字符串形式,也支持集合形式,当传的是一个list集合时,它会自动计算集合中所有文本相似度分数的平均值,这是通过参数avg=True来指定的。

下面改造批量评测的run_test_batch方法,返回的结果不再仅仅是labels集合,还包括了speakers和reasons集合。

from typing import List, Dict

def make_str(data):  
    return ','.join(map(str, data)) if isinstance(data, list) else data 

def make_results(dataset):
    return {
       "labels": [item.get('label', item.get('is_fraud', False)) for item in dataset],
        "speakers": [make_str(item.get('fraud_speaker', '')) for item in dataset],
        "reasons": [item.get('reason', '') for item in dataset],
    }


def run_test_batch_v2(model, tokenizer, test_data: List[Dict], batch_size: int = 8, device='cuda'):
    real_outputs, pred_outputs = [], []
    instruction = test_data[0].get('instruction', None)
    pbar = tqdm(total=len(test_data), desc=f'progress')
    
    for i in range(0, len(test_data), batch_size):
        batch_data = test_data[i:i + batch_size]
        batch_inputs = [item['input'] for item in batch_data]
        predictions = predict_batch(model, tokenizer, batch_inputs, device, instruction=instruction)
        
        real_outputs.extend(batch_data)
        pred_outputs.extend(predictions)
        
        pbar.update(len(batch_data))

    pbar.close()
    return make_results(real_outputs), make_results(pred_outputs)

扩展一个evaluate_v2方法,此方法中区分字段走不同的指标计算:

  • is_fraud: 通过sklean包的混淆矩阵来计算精确率和召回率;
  • fraud_speaker: 使用sklearn包提供的accuracy_score方法来计算准确率;
  • reason: 以上面封装的中文rouge分数作为评测指标;
from sklearn.metrics import accuracy_score 

def evaluate_with_model_v2(model, tokenizer, testdata_path, device='cuda', debug=False):
    dataset = load_jsonl(testdata_path)
    real_result, pred_result = run_test_batch_v2(model, tokenizer, dataset, device=device)
    
    precision, recall, accuracy = precision_recall(real_result["labels"], pred_result["labels"], debug=debug)
    print(f"is_fraud字段指标: \nprecision: {precision}, recall: {recall}, accuracy: {accuracy}")
    
    accuracy = accuracy_score(real_result["speakers"], pred_result["speakers"])  
    print(f"fraud_speaker字段指标: \naccuracy: {accuracy}")

    score = rouge_zh_score(pred_result["reasons"], real_result["reasons"])
    print(f"reason字段指标: \nprecision: {score['p']}, recall: {score['r']}, f1-score: {score['f']}")
    
    return real_result, pred_result
    

def evaluate_v2(model_path, checkpoint_path, testdata_path, device='cuda', debug=False):    
    model, tokenizer = load_model(model_path, checkpoint_path, device)
    return evaluate_with_model_v2(model, tokenizer, testdata_path, device, debug)

4. 基座模型测评

先对基座模型进行评测,后续则以基座模型的评测结果作为微调效果的评判依据。

os.environ["CUDA_VISIBLE_DEVICES"] = "0"
device = 'cuda'
testdata_path = '/data2/anti_fraud/dataset/test0902.jsonl'
model_path = '/data2/anti_fraud/models/modelscope/hub/Qwen/Qwen2-1___5B-Instruct'
real_result, pred_result = evaluate_v2(model_path, '', testdata_path, device, debug=True)
    invalid json: 不存在经济诈骗。
    
    分析结果:不存在经济诈骗。在提供的对话中,发言者4只是分享了一个关于商业需求预测的观点,并没有提到任何欺诈行为或企图欺骗他人的情况。因此,可以确定该对话不存在经济诈骗。

    
    经过分析,该对话中不存在明显的经济诈骗行为。发言人的言论主要围绕着旅游统计数据的准确性进行讨论,并提出了可能存在的误差来源。因此,可以判断此对话不存在经济诈骗行为。
    invalid json: 不存在经济诈骗。

    invalid json: 不存在经济诈骗。

    invalid json: 不存在经济诈骗。

    ……

以上分析结果表明,该对话中不存在明显的经济诈骗行为。所有发言者的言论都是关于产品的生产和销售情况的讨论,并且没有涉及到欺诈或非法活动。因此,可以得出结论:此对话不存在经济诈骗行为。
progress: 100%|██████████| 3031/3031 [17:55<00:00,  2.82it/s]
Building prefix dict from the default dictionary ...
Loading model from cache /tmp/jieba.cache
tn:590, fp:903, fn:44, tp:1494
is_fraud字段指标: 
precision: 0.623279098873592, recall: 0.9713914174252276, accuracy: 0.6875618607720224
fraud_speaker字段指标: 
accuracy: 0.6374133949191686
Loading model cost 0.776 seconds.
Prefix dict has been built successfully.
reason字段指标: 
precision: 0.33552894583785237, recall: 0.23240362412953733, f1-score: 0.2638127489291967

注:由于基座模型遵循指令的能力较弱,所以在预测时输出了很多json格式错误,为避免篇幅太长,上面省略和截掉了很多重复的错误的信息,并且在遇到错误时统一使用默认的{“is_fraud”: false}作为返回值。

指标收集如下:

字段指标
is_fraudprecision: 0.6232, recall: 0.9713, accuracy=0.6875
fraud_speakeraccuracy: 0.6374
reasonprecision: 0.3355, recall: 0.2324, f1-score: 0.2638

从上面的指标来看,只有is_fraud字段的 召回率0.9673比较高,但其它指标(如is_fraud字段的精确率,fraud_speaker字段的准确率,reason字段的各项指标)都比较低。

小结:本文通过对rouge指标的调研测试,了解了rouge指标中的rouge-1和rouge-L子指标的区别,并对评测脚本进行了改造升级,以支持分类原因和欺诈者字段的生成质量评测,为后续分类原因和欺诈者字段的训练微调提供了一个效果评测的基准。

参考阅读

  • 数据校正与增强
  • 模型评测
  • 批量评测改造
  • 大模型评估指标之ROUGE
  • jieba分词初探

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/884372.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于SpringBoot+Vue3的在线报名系统

一、项目介绍 1.1 项目介绍 本项目为一个报名系统&#xff0c;实现了基本的报名流程&#xff0c;功能完善&#xff0c;前后端皆有个人独立开发&#xff0c;功能相对不是特别难&#xff0c;但该有的功能还是都已经实现。 1.2 技术架构 主要技术栈&#xff1a; SpringBoot2 …

WebRTC中的维纳滤波器实现详解:基于决策导向的SNR估计

目录 1. 维纳滤波器的基本原理2. WebRTC中的维纳滤波器实现3. 代码逐步剖析4. 总结 在WebRTC的噪声抑制模块中&#xff0c;维纳滤波器&#xff08;Wiener Filter&#xff09;是一种非常常见且重要的滤波器&#xff0c;用于提高语音信号的清晰度并抑制背景噪声。本文将详细解释维…

erlang学习:Linux命令学习6

for循环学习 打印九九乘法表 for i in {1..9};do %%取1-9for j in $(seq 1 $i);do %%取1-iecho -n "$j*$i$((i*j)) " %%进行九九乘法表打印doneecho done尝试了很多次报错是因为后面的换行符不对&#xff0c;window系统中的换行符与linux对不上&#xff0c;因…

AI芯片WT2605C赋能厨房家电,在线对话操控,引领智能烹饪新体验:尽享高效便捷生活

在智能家居的蓬勃发展中&#xff0c;智能厨电作为连接科技与生活的桥梁&#xff0c;正逐步渗透到每一个现代家庭的厨房中。蒸烤箱作为智能厨电的代表&#xff0c;以其丰富的功能和高效的性能&#xff0c;满足了人们对美食的多样化追求。然而&#xff0c;面对众多复杂的操作功能…

OpenHarmony(鸿蒙南向)——平台驱动开发【MIPI DSI】

往期知识点记录&#xff1a; 鸿蒙&#xff08;HarmonyOS&#xff09;应用层开发&#xff08;北向&#xff09;知识点汇总 鸿蒙&#xff08;OpenHarmony&#xff09;南向开发保姆级知识点汇总~ 持续更新中…… 概述 功能简介 DSI&#xff08;Display Serial Interface&#x…

小阿轩yx-案例:代码管理系统简介与部署

小阿轩yx-案例&#xff1a;代码管理系统简介与部署 前言 开发一个项目时&#xff0c;如果只有几十行代码或几百行代码时维护还算简单&#xff0c;但是代码数量达到一定程度或两三个人共同开发一个项目时&#xff0c;就很容易会出现代码混乱、冲突、排错难等问题。代码编写完成…

【软件测试】如何设计测试用例? 设计测试用例常用的方法.

目录 一.什么是测试用例?二.总体设计测试用例的万能公式.2.1 功能性能界面兼容易用安全2.2 弱网测试2.3 安装卸载测试. 三. 常用设计具体测试用例的方法3.1 等价类3.2 边界值3.3 正交法3.3.1 正交表3.3.2 如何设计正交表,并根据正交表编写测试用例 3.4 判定表法3.4.1 根据判定…

828华为云征文 | 解锁高效项目管理,Zentao在华为云Flexusx容器化部署与应用指南

前言 在当今快速迭代的商业环境中&#xff0c;高效且灵活的项目管理成为企业竞争力的关键。华为云Flexusx实例&#xff0c;以其灵活的vCPU内存配比、热变配功能及按需计费模式&#xff0c;为项目管理软件如Zentao的部署提供了理想平台。Flexusx实例采用按需计费的灵活定价模式&…

Ansible流程控制-条件_循环_错误处理_包含导入_块异常处理

文章目录 Ansible流程控制介绍1. 条件判断2. 循环3. 循环控制4. 错误处理5. 包含和导入6. 块和异常处理7. 角色的流程控制*include_tasks、import_tasks_include之间的区别 条件语句再细说且、或、非、是模糊条件when指令的详细使用方法 循环语句再细说如何使用使用item变量结合…

甄选范文“论软件需求管理”,软考高级论文,系统架构设计师论文

论文真题 软件需求管理是一个对系统需求变更了解和控制的过程。需求管理过程与需求开发过程相互关联,初始需求导出的同时就要形成需求管理规划,一旦启动了软件开发过程,需求管理活动就紧密相伴。 需求管理过程中主要包含变更控制、版本控制、需求跟踪和需求状态跟踪等4项活…

???Ansible-使用roles

文章目录 一、Ansible的内置的或官方推荐创建的目录及文件介绍roles目录解释1、roles/自定义角色名目录下2、roles/自定义角色名目录/tasks目录下3、roles/自定义角色名目录/handlers目录下4、roles/自定义角色名目录/templates目录下5、roles/自定义项目名目录/files目录下6、…

SSM超市售卖管理系统-计算机毕业设计源码23976

目 录 摘要 Abstract 1 绪论 1.1研究的背景和意义 1.2研究内容 1.3论文结构与章节安排 2 开发技术介绍 2.1 SSM框架 2.2 MySQL数据库 3 超市售卖管理系统系统分析 3.1 可行性分析 3.2 系统流程分析 3.2.1 数据流程 3.3.2 业务流程 3.3 系统功能分析 3.3.1 功…

港科夜闻 | 香港科大颁授荣誉大学院士予五位杰出人士

关注并星标 每周阅读港科夜闻 建立新视野 开启新思维 1、香港科大颁授荣誉大学院士予五位杰出人士。香港科大9月24日向五位杰出人士颁授荣誉大学院士&#xff0c;他们分别为包弼德教授、简吴秋玉女士、高秉强教授、吴永顺先生及容永祺博士(按姓氏英文字母排序)。荣誉大学院士颁…

BUG——IMX6ULL编译正点原子Linux内核报错

最初编译的是正点原子改过的Linux内核&#xff0c;可能是版本问题&#xff0c;一直报错&#xff0c;无法成功编译。然后换成NXP官方Linux内核6.6版本&#xff0c;初始编译虽然也报各种错&#xff0c;但都是缺少库或相关工具&#xff0c;全部安装后就可以成功编译出镜像了&#…

WiFi无线连接管理安卓设备工具:WiFiADB

介绍 WiFi ADB 使您能够通过 WiFi TCP/IP 连接直接在设备上轻松调试和测试 Android 应用&#xff0c;无需使用 USB 数据线。在启用 WiFi 上的 ADB 后&#xff0c;打开控制台将电脑连接到设备。 手机和电脑在同一个WiFi然后电脑上运行adb connect x.x.x.x:x命令即可 下载 谷…

IoT网关的主要功能有哪些?天拓四方

在数字化浪潮席卷全球的今天&#xff0c;物联网&#xff08;IoT&#xff09;技术凭借其独特的优势&#xff0c;逐渐在各个领域展现出强大的生命力。而IoT网关&#xff0c;作为连接物理世界与数字世界的桥梁&#xff0c;其在物联网体系中的作用愈发凸显。 一、数据聚合与预处理…

leetcode每日一题day15(24.9.25)——公司命名

思路&#xff1a;首先如果没有相同的后缀&#xff0c;则无论只要不是相同的首字母交换都不会出现重复情况&#xff0c;如果有重复后缀&#xff0c;则还需多增加个不能和&#xff0c;首字符与另一相同后缀字串的首字符相同的字串交换。 主要矛盾已经明确&#xff0c;则可对矛盾…

Redis集群的两种方式

1.Redis集群 1.1 搭建主从集群 单节点Redis的并发能力是有上限的&#xff0c;要进一步提高Redis的并发能力&#xff0c;就需要搭建主从集群&#xff0c;实现读写的分离。一般情况下&#xff0c;主节点负责写操作&#xff0c;从节点负责读操作。而从节点如何得知数据呢&#xff…

SpringBoot文档管理系统:架构与功能

第2章相关技术 2.1 Java技术介绍 Java语言擅长开发互联网类应用和企业级应用&#xff0c;现在已经相当的成熟&#xff0c;而且也是目前使用最多的编程语言之一。Java语言具有很好的面向对象性&#xff0c;可以符合人的思维模式进行设计&#xff0c;封装是将对象的属性和方法尽可…

【4.6】图搜索算法-DFS和BFS解合并二叉树

一、题目 给定两个二叉树&#xff0c;想象当你将它们中的一个覆盖到另一个上时&#xff0c;两个二叉树的一些节点便会重叠。你需要将他们合并为一个新的二叉树。合并的规则是 如果两个节点重叠&#xff0c;那么将他们的 值相加作为节点合并后的新值&#xff0c;否则不为 NUL L…