基于星火大模型的群聊对话分角色要素提取挑战赛|#AI夏令营#Datawhale#夏令营-Lora微调与prompt构造

赛题连接

https://challenge.xfyun.cn/topic/info?type=role-element-extraction&option=phb

Datawhale Al夏令营 零基础入门大模型技术竞赛

在这里插入图片描述

数据集预处理

由于赛题官方限定使用了星火大模型,所以只能调用星火大模型的API或者使用零代码微调
首先训练数据很少是有129条,其中只有chat_textinfos两个属性,chat_text是聊天文本,infos就是提取的信息也是训练集标签,他的平均长度有6000左右对于星火对于信息提取任务已经很长了,而且最长的将近30000,如果使用星火大模型进行询问肯定是要被截断的,而且微调上传的数据也是有最大长度的,我门需要对数据进行处理。
请添加图片描述

数据简单清洗

简单的导包

from dataclasses import dataclass
from sparkai.llm.llm import ChatSparkLLM, ChunkPrintHandler
from sparkai.core.messages import ChatMessage
import pandas as pd
import os
import json
import re
import matplotlib.pyplot as plt
from tqdm import tqdm
from math import ceil
import numpy as np
from copy import deepcopy
import randomtqdm.pandas()
plt.rcParams['font.family'] = ['STFangsong']
plt.rcParams['axes.unicode_minus'] = False

加载数据

data_dir = "./data"
train_file = "train.json"
test_file = "test_data.json"train_data = pd.read_json(os.path.join(data_dir, train_file))
test_data =  pd.read_json(os.path.join(data_dir, test_file))

首先我们发现数据集中有许多[图片]超链接,这些对数据提取作用不大,我们可以将其去掉,

# 删除表情图片、超链接
train_data['chat_text'] = train_data['chat_text'].str.replace(r"\[[^\[\]]{2,10}\]", "", regex=True)
train_data['chat_text'] = train_data['chat_text'].str.replace("https?://\S+", "", regex=True)
test_data['chat_text'] = test_data['chat_text'].str.replace(r"\[[^\[\]]{2,10}\]", "", regex=True)
test_data['chat_text'] = test_data['chat_text'].str.replace("https?://\S+", "", regex=True)

对于一个人连续的对话我们可以哦将其合并成一个对话

def get_names_phones_and_emails(example):names = re.findall(r"(?:\n)?([\u4e00-\u9fa5]+\d+):", example["chat_text"])names += re.findall(r"@([\u4e00-\u9fa5]+)\s", example["chat_text"])emails = re.findall(r"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}", example["chat_text"])# phones = re.findall(r"1[356789]\d{9}", example["chat_text"]) # 文本中的手机号并不是标准手机号phones = re.findall(r"\d{3}\s*\d{4}\s*\d{4}", example["chat_text"]) return pd.Series([set(names), set(phones), set(emails)], index=['names', 'phones', 'emails'])def merge_chat(example):for name in example['names']:example["chat_text"] = example["chat_text"].replace(f"\n{name}:", f"<|sep|>{name}:")chats = example["chat_text"].split("<|sep|>")last_name = "UNKNOWN"new_chats = []for chat in chats:if chat.startswith(last_name):chat = chat.strip("\n")chat = "".join(chat.split(":")[1:])new_chats[-1] += " " + chatelse:new_chats.append(chat)last_name = chat.split(":")[0]return pd.Series(["\n".join(new_chats), new_chats], index=["chats", "chat_list"])# 使用正则表达式获得'names', 'phones', 'emails'
train_data[['names', 'phones', 'emails']] = train_data.apply(get_names_phones_and_emails, axis=1)
test_data[['names', 'phones', 'emails']] = test_data.apply(get_names_phones_and_emails, axis=1)
# 分割聊天记录, 合并连续相同人的聊天
train_data[["chats", "chat_list"]] = train_data.apply(merge_chat, axis=1)
test_data[["chats", "chat_list"]] = test_data.apply(merge_chat, axis=1)

请添加图片描述

补充

补充:后面我们发现数据中chat_text中有许多是重复多编的,我们需要把重复的也给去除掉,这样处理后的数据就会大大减小,使用暴力匹配去除
请添加图片描述

def process(excemple):chat_list = excemple["chat_text"].split("\n")res = []s = 0while s < len(chat_list):i, j = s, s+1start_j = jwhile i < len(chat_list) and j < len(chat_list):if chat_list[i] == chat_list[j]:i += 1else:if i != s:if j - start_j >10:res += list(range(start_j, j))i = sstart_j = jj += 1s += 1texts = []for i in range(len(chat_list)):if i not in res:texts.append(chat_list[i])return "\n".join(texts)train_data["chat_text"] = train_data.apply(process, axis = 1)
test_data["chat_text"] = test_data.apply(process, axis = 1)

构造训练集

处理之后其实有些还是很长,我们可以有两种简单粗暴的方法

  1. 截断
  2. 分块
    对于构造训练数据,我们使用了第一种截断的方法,但这两种方法都有一定的缺点
    我们需要查看讯飞官方微调需要的训练集格式,这里我选择使用JSONL格式,并且其每一行是一个JSON字符串,格式为
{"input":"", "target":""}

在这里插入图片描述
训练时我选用了讯飞的spark pro进行训练,其要求训练数据不少于1500条,每一个input+target长度不能大于8000

def process(x):# 提示词,我们交代清楚大模型的角色、目标、注意事项,然后提供背景信息,输出格式就可以了prompt = f"""Instruction:
你是一个信息要素提取工作人员,你需要从给定的`ChatText`中提取出**客户**的`Infos`中相关信息,将提取的信息填到`Infos`中,
注意事项:
1. 没有的信息无需填写
2. 保持`Infos`的JSON格式不变,没有的信息项也要保留!!!
4. 姓名可以是聊天昵称
5. 注意是客户的信息,不是客服的信息
6. 可以有多个客户信息
ChatText:
{x["chat_text"]}
"""# 要求的输出格式infos = """"
Infos:
infos": [{"基本信息-姓名": "","基本信息-手机号码": "","基本信息-邮箱": "","基本信息-地区": "","基本信息-详细地址": "","基本信息-性别": "","基本信息-年龄": "","基本信息-生日": "","咨询类型": [],"意向产品": [],"购买异议点": [],"客户预算-预算是否充足": "","客户预算-总体预算金额": "","客户预算-预算明细": "","竞品信息": "","客户是否有意向": "","客户是否有卡点": "","客户购买阶段": "","下一步跟进计划-参与人": [],"下一步跟进计划-时间点": "","下一步跟进计划-具体事项": ""
}]
"""# prompt+infos是文件中的input,answer是文件中的targetanswer = f"""{x["infos"]}""" #targettotal= len(prompt + infos + answer)if total > 8000:prompt = prompt[:8000-len(infos + answer)]return pd.Series([prompt, answer], index=["input", "target"])data = train_data.apply(process, axis=1)
# 测试集中的target并没有用可以忽略
data = test_data.apply(process, axis=1)#保存数据
with open(os.path.join(data_dir, "my_train.jsonl"), "w", encoding="utf-8") as f:f.write("\n".join([json.dumps(i, ensure_ascii=False) for i in list(data.transpose().to_dict().values())]))
f.close()
with open(os.path.join(data_dir, "my_test.jsonl"), "w", encoding="utf-8") as f:f.write("\n".join([json.dumps(i, ensure_ascii=False) for i in list(data.transpose().to_dict().values())]))
f.close()

对于训练数据不少于1500条的要求,我直接将训练集进行了多次复制,只要不少于1500条就可以训练。训练我只训练了两轮。
在这里插入图片描述

使用官方零代码微调

在这里插入图片描述

测试

模型训练好后我们需要到官网将训练好的模型发布,这样才能够调用
在这里插入图片描述
在这里插入图片描述
在我的服务中获取 接口地址APPIDAPIKeyAPISecret,不同版本会有不同

在这里插入图片描述
后续就可以写代码测试了,我们可以询问多轮然后进行投票,减少一次不确定性带来的误差,一轮其实已经可以达到26以上的分数了

from sparkai.llm.llm import ChatSparkLLM, ChunkPrintHandler
from sparkai.core.messages import ChatMessage
import pandas as pd
import os
from tqdm import tqdm
import jsonspark = ChatSparkLLM(spark_api_url="wss://spark-api-n.xf-yun.com/v3.1/chat",#spark pro微调的urlspark_app_id="",spark_api_key="",spark_api_secret="",spark_llm_domain="patchv3", #spark pro微调的版本streaming=False,
)
def save_result(data):with open("./data/result1.json", "w") as f:file = data.to_json(orient='records', index=False, force_ascii=False)f.write(file)f.close()
for j in range(0, 10):res = []for i in tqdm(range(len(data)), desc=f"正在询问第{j}轮"):messages = [ChatMessage(role="user",content=data.iloc[i]["input"])]while True:try:handler = ChunkPrintHandler()a = spark.generate([messages], callbacks=[handler])a = json.loads(a.generations[0][0].text.replace("'", "\""))except:print("出错了")continueres.append(a)breakmulti_res.append(res)test_data[f"infos_{j}"] = ressave_result(test_data)

多轮投票

from typing import Counter, defaultdicttemplate_infos = {"基本信息-姓名": "","基本信息-手机号码": "","基本信息-邮箱": "","基本信息-地区": "","基本信息-详细地址": "","基本信息-性别": "","基本信息-年龄": "","基本信息-生日": "","咨询类型": [],"意向产品": [],"购买异议点": [],"客户预算-预算是否充足": "","客户预算-总体预算金额": "","客户预算-预算明细": "","竞品信息": "","客户是否有意向": "","客户是否有卡点": "","客户购买阶段": "","下一步跟进计划-参与人": [],"下一步跟进计划-时间点": "","下一步跟进计划-具体事项": ""
}
result_Infos = []
## 这里的代码已经不是我最初始的代码了,可能会影响到效果,最初我是不管有结果个用户,只投出一个用户,其他信息也是直接全部投票,没有使用根据'基本信息-姓名'进行分开投票,可以自行尝试,投票还是可以提升一点分数的
for multi_infos in zip(*multi_res):names_info_dict = defaultdict(list)for infos in multi_infos:for info in infos:names_info_dict[info['基本信息-姓名']].append(info)res_infos = []for name in names_info_dict:l = len(names_info_dict[name])print(l)if l < 5:continueinfos = template_infos.copy()for attr in template_infos:if isinstance(template_infos[attr], str):val_freq = Counter([multi_info.get(attr, "") for multi_info in names_info_dict[name]])top_2 = val_freq.most_common(2)if len(top_2) == 1:val = top_2[0][0]else:if top_2[0][0] == "" and top_2[1][1] < l/2:val = ""elif top_2[0][0] == "":val = top_2[1][0]else:val = top_2[0][0] else:val_freq = []for multi_info in names_info_dict[name]:val_freq.extend((multi_info.get(attr, [])))val_freq = Counter(val_freq)val =[val for val, freq in val_freq.most_common(10) if freq > l/2]infos[attr] = valres_infos.append(infos)# if len(res_infos) >= 2:#     print(len(names_info_dict[name]),res_infos)result_Infos.append(res_infos)
test_data["infos"] = result_Infos
save_result(test_data[["chat_text", "infos"]])

总结

以上只是一个简洁的思路,如果有其他想法欢迎在评论区留言。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

周志华机器学习西瓜书经典来袭!PPT+课件+南瓜书

南京大学教授&#xff0c;博士生导师&#xff0c;教育部“长江学者”特聘教授&#xff0c;国家杰出青年基金获得者&#xff01;周志华老师的头衔太多了&#xff01;相信每一个从事或爱好机器学习的朋友都知道周志华老师&#xff0c;尤其是他的那本经典巨作《机器学习》&#xf…

慧哥Saas充电桩开源平台 V2.5.5

文章目录 原地址&#xff1a;https://gitee.com/chouleng/cdzkjjh&#xff0c;更换新的地址如下 [点击此链接 https://gitee.com/chouleng/huili-cloud](https://gitee.com/chouleng/huili-cloud)一、产品功能部分截图1.手机端&#xff08;小程序、安卓、ios&#xff09;2.PC端…

华为云OBS 通过S3客户端访问

华为云好像没有对S3协议的支持说明其实底层是支持S3协议的。 使用S3的时候我们会需要endpoint&#xff0c;桶名字&#xff0c;region&#xff0c;AWS_ACCESS_KEY,AWS_SECRET_KEY 其中endpoint 就是图片中的&#xff0c;桶名字也很容易找到&#xff0c;region 就是你的endpoint…

博途TIA Portal「集成自动化软件」下载安装,TIA Portal 灵活多变的编程环境

在编程领域&#xff0c;博途TIA Portal以其卓越的编程工具和灵活多变的编程环境&#xff0c;为众多用户提供了前所未有的便利。这款软件不仅支持多种编程语言&#xff0c;如梯形图&#xff08;Ladder Diagram&#xff09;、功能块图&#xff08;Function Block Diagram&#xf…

华为HCIP Datacom H12-821 卷24

1.单选题 企业大楼有大量员工通常都在上班时在大厅开始接入到公司的WLAN网络,随着每位员工走到各自的工位过程中&#xff0c;每个人的移动端叶通过漫游的方式漫游到各自的网络覆盖区域。为了尽量保证每个终端的IP地址是固定的&#xff0c;建议的做法是? A、配置VLAN Poo…

统计信号处理基础 习题解答11-13

题目 如果是一个2x1的随机矢量&#xff0c;具有PDF 证明的PDF是一个随机变量。提可以因式分解成&#xff0c;其中是一个在4.5节描述的白化变换。 解答 首先&#xff1a; 因此&#xff0c;存在&#xff1a; 也就是是Hermitian矩阵。详细的性质可以参考&#xff1a; https://z…

抠图怎么抠?教你3种一看就会的抠图工具

抠图怎么抠&#xff1f;抠图&#xff0c;作为图像处理中的一项基本而强大的技能&#xff0c;广泛应用于摄影后期、广告设计、影视特效等多个领域。它能够将图像中的特定对象或区域从背景中精确分离出来&#xff0c;便于后续编辑或与其他图像合成。随着科技的发展&#xff0c;现…

个人PayPal账户与企业PayPal账户:差异与选择

PayPal作为全球领先的在线支付平台&#xff0c;为不同用户群体设计了两种类型的账户&#xff1a;个人账户和企业账户&#xff0c;不仅为个人用户提供了便捷的支付和收款服务&#xff0c;同时也为企业用户提供了丰富的电子商务解决方案&#xff0c;让个人和企业都能使用便捷的电…

实现高效写入:Schemaless 写入性能优化指南

物联网应用常常需要收集大量的数据&#xff0c;用以支持智能控制、业务分析和设备监控等功能。然而&#xff0c;应用逻辑的更新或硬件的调整可能会导致数据采集项频繁变化&#xff0c;这是时序数据库&#xff08;Time Series Database&#xff0c;TSDB&#xff09;面临的一大挑…

vue中自定义设置多语言,并且运行js脚本自动生成多语言文件

在项目中需要进行多个国家语言的切换时&#xff0c;可以用到下面方法其中一个 一、自定义设置多语言 方法一: 可以自己编写一个设置多语言文件 在项目新建js文件&#xff0c;命名为&#xff1a;language.js&#xff0c;代码如下 // language.js 文档 let languagePage {CN…

红酒与舞蹈:舞动的味觉艺术

在艺术的海洋中&#xff0c;红酒与舞蹈总是能激起人们心中较温柔的涟漪。红酒以其深邃的色泽、馥郁的香气&#xff0c;诠释着味觉的艺术&#xff1b;而舞蹈&#xff0c;则以优雅的姿态、灵动的步伐&#xff0c;演绎着视觉的盛宴。当红酒遇上舞蹈&#xff0c;一场别开生面的艺术…

少见的更优写法,反转字符串中的元音字母

Leetcode 原题链接 解法一 这道题很简单&#xff0c;令双指针 l l l 和 r r r 从两侧相向移动&#xff0c;交换元音字母即可。但大多人的实现是如下这种可简化的嵌套循环。 如果是 Java 等 String 不可变的语言&#xff0c;应先转换为 CharArray&#xff0c;交换完元音字母…

家用洗地机什么牌子好?四款公认品牌好的机型推荐

每个人都希望自己的家里面能够干干净净&#xff0c;就算不是一尘不染&#xff0c;也至少应该是整洁的&#xff0c;但是在这个快节奏的大环境下&#xff0c;做清洁对于人们来说&#xff0c;不是没时间&#xff0c;就是太累了。正当此时&#xff0c;一款造福懒人的神器——家用洗…

4D 生物打印技术的挑战:从打印到植入,还有多远?

4D生物打印技术将时间维度融入生物打印&#xff0c;为构建具有动态特性和功能的生物组织结构提供了无限可能。然而&#xff0c;要实现这些目标&#xff0c;选择合适的生物打印技术至关重要。本文将详细介绍几种主要的4D生物打印技术&#xff0c;并分析它们各自的优缺点&#xf…

前端初学java二(类、多态、接口、内部类、泛型)

目录 类 种类 Javabean类 测试类 工具类 类的初始化 构照函数 新建对象的内存图 static 继承 This Super 虚方法表 Override 修饰符权限 构造代码块 静态代码块 多态 前提 优点 缺点 示例 抽象方法 抽象类 接口 implements 继承 内部类 成员内部类…

React+TS 从零开始教程(4):useEffect

上一节传送门&#xff1a;ReactTS 从零开始教程&#xff08;3&#xff09;&#xff1a;useState 源码链接&#xff1a;https://pan.quark.cn/s/c6fbc31dcb02 上一节&#xff0c;我们已经学会了React的第一个Hook&#xff1a;useState。 这一节&#xff0c;我们要学习的是另一…

C语言----文件操作

1.为什么使用文件&#xff1f; 如果没有⽂件&#xff0c;我们写的程序的数据是存储在电脑的内存中&#xff0c;如果程序退出&#xff0c;内存回收&#xff0c;数据就丢失了&#xff0c;等再次运⾏程序&#xff0c;是看不到上次程序的数据的&#xff0c;如果要将数据进⾏持久化…

Java语言开发的一套智慧产科系统源码:产科专科电子病历系统源码

Java语言开发的一套智慧产科系统源码&#xff1a;产科专科电子病历系统源码 系统概述 电子病历系统是以住院病人为中心&#xff0c;面向医生以及护士为主的&#xff0c;涉及临床治疗、护理等业务的临床信息系统&#xff0c;以电子信息技术为手段&#xff0c;实时采集病人在整个…

【每日一练】Python遍历循环

1. 情节描述&#xff1a;上公交车(10个座位)&#xff0c;并且有座位就可以坐下 要求&#xff1a;输入公交卡当前的余额&#xff0c;只要超过2元&#xff0c;就可以上公交车&#xff1b;如果车上有空座位&#xff0c;才可以上。 seat 10 while seat > 0:money int(input(…

分层解耦----

分层解耦 类聚 软件中各个功能模块内部的功能联系. 例如: 高类聚示例&#xff1a;想象一下餐厅的厨房&#xff0c;每个厨师负责自己的工作站&#xff0c;一个专门做沙拉&#xff0c;一个专门烤肉&#xff0c;另一个专门做甜点。每个工作站内的工作高度类聚&#xff0c;即每个…