NeMo Curator 整理用于 LLM 参数高效微调的自定义数据集

目录

概述

预备知识

定义自定义文档构建器

下载数据集

解析和迭代数据集

将数据集写入 JSONL 格式

使用文档构建器加载数据集

使用现有工具统一 Unicode 格式

设计自定义数据集过滤器

编辑所有个人识别信息

添加指令提示

整合管线


概述

出于演示目的,本文重点介绍一个涉及电子邮件分类的玩具示例。目标是整理一个基于文本的小型数据集,其中每个记录都包含电子邮件(主题和正文)以及该电子邮件的预定义分类标签。

为此,我们使用了 Enron 电子邮件数据集,将每封电子邮件标记为八个类别之一。此数据集可在 Hugging Face 上公开获取,并包含约 1400 条记录。

数据管护流程涉及以下高级步骤:

  1. 定义下载器、迭代器和提取器类,将数据集转换为 JSONL 格式。
  2. 使用现有工具统一 Unicode 表示。
  3. 定义自定义数据集过滤器,以删除空白或过长的电子邮件。
  4. 编辑数据集中的所有个人识别信息 (PII)。
  5. 为每条记录添加指令提示。
  6. 整合整个管线。

在消费级硬件上执行此策展制作流程需要不到 5 分钟的时间。要访问本教程的完整代码,请参阅 NVIDIA/NeMo-Curator GitHub 资源库。

预备知识

开始之前,您必须安装 NeMo Curator 框架。按照 NeMo Curator GitHub README 文件中的说明安装该框架。

接下来,运行以下命令以验证安装并安装任何其他依赖项:

$ python -c "import nemo_curator; print(nemo_curator);"
$ pip3 install requests

定义自定义文档构建器

整理数据集的第一步是实现文档构建器,以便下载并迭代数据集。

下载数据集

实现DocumentDownloader类获取数据集的 URL,并使用requests库。

import requests
from nemo_curator.download.doc_builder import DocumentDownloaderclass EmailsDownloader(DocumentDownloader):def __init__(self, download_dir: str):super().__init__()if not os.path.isdir(download_dir):os.makedirs(download_dir)self._download_dir = download_dirprint("Download directory: ", self._download_dir)def download(self, url: str) -> str:filename = os.path.basename(url)output_file = os.path.join(self._download_dir, filename)if os.path.exists(output_file):print(f"File '{output_file}' already exists, skipping download.")return output_fileprint(f"Downloading Enron emails dataset from '{url}'...")response = requests.get(url)with open(output_file, "wb") as file:file.write(response.content)return output_file

下载的数据集是一个文本文件,每个条目大致遵循以下格式:

“<s>[system instruction prompts]Subject:: [email subject]
Body:: [email body][category label] <s>”

您可以使用正则表达式轻松地将这种格式分解为其组成部分。要记住的关键是,条目由“<s> … <s>”并且始终以指令提示开始。此外,示例分隔符令牌和系统提示令牌与 Llama 2 标记器系列兼容。

由于您可能会将这些数据与不支持特殊令牌的其他分词器或模型一起使用,因此最好在解析期间丢弃这些指令和令牌。在本文的稍后部分中,我们将展示如何使用 NeMo Curator 将指令提示或特殊令牌添加到每个条目中DocumentModifier实用程序。

解析和迭代数据集

实现DocumentIteratorDocumentExtractor用于提取电子邮件主题、正文和类别 (类) 标签的类:

from nemo_curator.download.doc_builder import (DocumentExtractor,DocumentIterator,
)class EmailsIterator(DocumentIterator):def __init__(self):super().__init__()self._counter = -1self._extractor = EmailsExtractor()# The regular expression pattern to extract each email.self._pattern = re.compile(r"\"<s>.*?<s>\"", re.DOTALL)def iterate(self, file_path):self._counter = -1file_name = os.path.basename(file_path)with open(file_path, "r", encoding="utf-8") as file:lines = file.readlines()# Ignore the first line which contains the header.file_content = "".join(lines[1:])# Find all the emails in the file.it = self._pattern.finditer(file_content)for email in it:self._counter += 1content = email.group().strip('"').strip()meta = {"filename": file_name,"id": f"email-{self._counter}",}extracted_content = self._extractor.extract(content)# Skip if no content extractedif not extracted_content:continuerecord = {**meta, **extracted_content}yield recordclass EmailsExtractor(DocumentExtractor):def __init__(self):super().__init__()# The regular expression pattern to extract subject/body/label into groups.self._pattern = re.compile(r"Subject:: (.*?)\nBody:: (.*?)\n.*\[/INST\] (.*?) <s>", re.DOTALL)def extract(self, content: str) -> Dict[str, str]:matches = self._pattern.findall(content)if not matches:return Nonematches = matches[0]return {"subject": matches[0].strip(),"body": matches[1].strip(),"category": matches[2].strip(),}

迭代器使用正则表达式,\"<s>.*?<s>\"然后,它将字符串传递给提取器,提取器使用正则表达式"Subject:: (.*?)\nBody:: (.*?)\n.*\[/INST\] (.*?) <s>"此表达式使用分组运算符(.*?)提取主题、正文和类别。

这些提取的部分以及有用的元数据(例如每封电子邮件的唯一 ID)存储在字典中,并返回给调用者。

现在,您可以将此数据集转换为 JSONL 格式,这是 NeMo Curator 支持的多种格式之一

将数据集写入 JSONL 格式

数据集以纯文本文件的形式下载。DocumentIteratorDocumentExtractor用于迭代记录的类,将其转换为 JSONL 格式,并将每条记录作为一行存储在文件中。

import jsondef download_and_convert_to_jsonl() -> str:"""Downloads the emails dataset and converts it to JSONL format.Returns:str: The path to the JSONL file."""# Download the dataset in raw format and convert it to JSONL.downloader = EmailsDownloader(DATA_DIR)output_path = os.path.join(DATA_DIR, "emails.jsonl")raw_fp = downloader.download(DATASET_URL)iterator = EmailsIterator()# Parse the raw data and write it to a JSONL file.with open(output_path, "w") as f:for record in iterator.iterate(raw_fp):json_record = json.dumps(record, ensure_ascii=False)f.write(json_record + "\n")return output_path

数据集中每条记录的信息都写入多个 JSON 字段:

  • subject
  • body
  • category
  • Metadata:
    • id
    • filename

这一点很有必要,因为 NeMo Curator 中的许多数据管护操作必须知道要在每个记录中操作哪个字段。这一结构允许 NeMo Curator 操作轻松地定位不同的数据集信息。

使用文档构建器加载数据集

在 NeMo Curator 中,数据集表示为类型对象DocumentDataset.这提供了从磁盘加载各种格式的数据集的辅助工具。使用以下代码加载数据集并开始使用:

from nemo_curator.datasets import DocumentDataset
# define `filepath` to be the path to the JSONL file created above.
dataset = DocumentDataset.read_json(filepath, add_filename=True)

您现在拥有了定义自定义数据集策管线和准备数据所需的一切。

使用现有工具统一 Unicode 格式

通常最好修复数据集中的所有 Unicode 问题,因为从在线来源抓取的文本可能包含不一致或 Unicode 错误。

为了修改文档,NeMo Curator 提供了一个DocumentModifier界面以及Modify辅助程序,用于定义如何修改每个文档中的给定文本。有关实现您自己的自定义文档修改器的更多信息,请参阅文本清理和统一在上一篇文章中看到的部分内容。

在本示例中,应用UnicodeReformatter到数据集。由于每条记录都有多个字段,因此请对数据集中的每个相关字段应用一次操作。这些操作可以通过Sequential类:

Sequential([Modify(UnicodeReformatter(), text_field="subject"),Modify(UnicodeReformatter(), text_field="body"),Modify(UnicodeReformatter(), text_field="category"),
])

设计自定义数据集过滤器

在许多 PEFT 用例中,优化数据集涉及过滤掉可能无关紧要或质量较低的记录,或者那些具有特定不合适属性的记录。在电子邮件数据集中,有些电子邮件过长或为空。出于演示目的,通过实现自定义,从数据集中删除所有此类记录DocumentFilter类:

from nemo_curator.filters import DocumentFilterclass FilterEmailsWithLongBody(DocumentFilter):"""If the email is too long, discard."""def __init__(self, max_length: int = 5000):super().__init__()self.max_length = max_lengthdef score_document(self, text: str) -> bool:return len(text) <= self.max_lengthdef keep_document(self, score) -> bool:return scoreclass FilterEmptyEmails(DocumentFilter):"""Detects empty emails (either empty body, or labeled as empty)."""def score_document(self, text: str) -> bool:return (not isinstance(text, str)  # The text is not a stringor len(text.strip()) == 0  # The text is emptyor "Empty message" in text  # The email is labeled as empty)def keep_document(self, score) -> bool:return score

我们FilterEmailsWithLongBodyclass 会计算所提供文本中的字符数,并返回True如果长度是可以接受的,或False否则。您必须在body每个记录的字段。

我们FilterEmptyEmails类检查给定文本的类型和内容,以确定其是否为空电子邮件,并返回True如果电子邮件被视为空白,或者False否则。您必须在所有相关字段中明确应用此过滤器:subjectbody以及category每条记录的字段。

返回值与类的命名一致,可提高代码的可读性。但是,由于目标是丢弃空电子邮件,因此必须反转此过滤器的结果。换言之,如果过滤器返回,则丢弃记录True并在过滤器返回时保留记录False.这可以通过提供相关标志来完成ScoreFilter辅助程序:

Sequential([# Apply only to the `body` field.ScoreFilter(FilterEmailsWithLongBody(), text_field="body", score_type=bool),# Apply to all fields, also invert the action.ScoreFilter(FilterEmptyEmails(), text_field="subject", score_type=bool, invert=True),ScoreFilter(FilterEmptyEmails(), text_field="body", score_type=bool, invert=True),ScoreFilter(FilterEmptyEmails(), text_field="category", score_type=bool, invert=True),
])

指定标志invert=True来指示ScoreFilter丢弃过滤器返回的文档True.通过指定 score_type=bool为每个过滤器明确指定返回类型,以避免在执行期间进行类型推理。

编辑所有个人识别信息

接下来,定义处理步骤,以编辑每个记录主题和正文中的所有个人识别信息 (PII)。此数据集包含许多 PII 实例,例如电子邮件、电话或传真号码、姓名和地址。

借助 NeMo Curator,您可以轻松指定要检测的个人身份信息(PII)类型以及对每次检测采取的操作。使用特殊令牌替换每个检测:

def redact_pii(dataset: DocumentDataset, text_field) -> DocumentDataset:redactor = Modify(PiiModifier(supported_entities=["ADDRESS","EMAIL_ADDRESS","LOCATION","PERSON","URL","PHONE_NUMBER",],anonymize_action="replace",device="cpu",),text_field=text_field,)return redactor(dataset)

您可以将这些运算应用到subjectbody使用 Pythonfunctools.partial辅助程序:

from functools import partialredact_pii_subject = partial(redact_pii, text_field="subject")
redact_pii_body = partial(redact_pii, text_field="body")Sequential([redact_pii_subject,redact_pii_body,]
)

添加指令提示

数据管护流程的最后一步是向每条记录添加指令提示,并确保每个类别的值都以句点终止。通过实现相关的DocumentModifier类:

from nemo_curator.modifiers import DocumentModifierclass AddSystemPrompt(DocumentModifier):def modify_document(self, text: str) -> str:return SYS_PROMPT_TEMPLATE % textclass AddPeriod(DocumentModifier):def modify_document(self, text: str) -> str:return text + "."

在代码示例中,SYS_PROMPT_TEMPLATE变量包含一个格式字符串,可用于在文本周围添加指令提示。这些修改器可以链接在一起:

Sequential([Modify(AddSystemPrompt(), text_field="body"),Modify(AddPeriod(), text_field="category"),
])

整合管线

在实现管线的每个步骤后,是时候将所有内容放在一起并按顺序对数据集应用每个操作了。您可以使用Sequential将类到链式管理操作结合在一起:

curation_steps = Sequential([## Unify the text encoding to Unicode.#Modify(UnicodeReformatter(), text_field="subject"),Modify(UnicodeReformatter(), text_field="body"),Modify(UnicodeReformatter(), text_field="category"),## Filtering#ScoreFilter(FilterEmptyEmails(), text_field="subject", score_type=bool, invert=True),ScoreFilter(FilterEmptyEmails(), text_field="body", score_type=bool, invert=True),ScoreFilter(FilterEmptyEmails(), text_field="category", score_type=bool, invert=True),ScoreFilter(FilterEmailsWithLongBody(), text_field="body", score_type=bool),## Redact personally identifiable information (PII).#redact_pii_subject,redact_pii_body,## Final modifications.#Modify(AddSystemPrompt(), text_field="body"),Modify(AddPeriod(), text_field="category"),]
)dataset = curation_steps(dataset)
dataset = dataset.persist()
dataset.to_json("/output/path", write_to_filename=True)

NeMo Curator 使用 Dask 以分布式方式处理数据集。由于 Dask 操作是延迟评估的,因此您必须调用.persist用于指示 Dask 应用操作的函数。处理完成后,您可以通过调用.to_json并提供输出路径。

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

6芯7芯可旋转电连接器航空插头

概述 可旋转电航空插头是一种能够在旋转或相对运动的部件间稳定传输电气信号或电源的装置&#xff0c;广泛应用于航空航天、自动化设备、医疗设备等多个领域。它的核心在于精密的接触系统&#xff0c;由旋转端和固定端两部分组成&#xff0c;通过金属触点或导电环实现电气连接。…

哪些网站用python开发

国内的话&#xff0c;知乎&#xff0c;网易&#xff0c;腾讯&#xff0c;搜狐&#xff0c;金山&#xff0c;豆瓣这些属于用Python比较知名的。大型的项目的话&#xff0c;网易的许多游戏&#xff0c;腾讯的某些网站&#xff0c;搜狐的邮箱&#xff0c;金山的测试框架等等都是或…

实习期间git的分枝管理以及最常用的命令

各位找工作实习的友友在工作之前一定要把git的相关知识掌握呀&#xff0c;我实现期间被leader说过关于git规范的相关问题了 目前已更新系列&#xff1a; 当前&#xff1a;:实习期间git的分枝管理以及最常用的命令 Redis高级-----持久化AOF、RDB原理 Redis高级---面试总结5种…

【JavaEE初阶】多线程(4)

欢迎关注个人主页&#xff1a;逸狼 创造不易&#xff0c;可以点点赞吗~ 如有错误&#xff0c;欢迎指出~ 目录 线程安全的 第四个原因 代码举例: 分析原因 解决方法 方法1 方法2 wait(等待)和notify(通知) wait和sleep区别 线程安全的 第四个原因 内存可见性,引起的线程安全问…

springboot3.X版本集成mybatis遇到的问题

由于我本地springboot为3.x版本&#xff0c;如下图所示&#xff0c;最新版本 当我参照如下搜索的内容去集成mybatis的时候&#xff0c;会出现各种各样的报错 最根本的原因是搜出来的配置是参照springboot2.X版本&#xff0c;当我们使用springboot3.x版本之后&#xff0c;需要配…

JVM 垃圾回收机制和GC案例分析

1. 引言 Java 虚拟机&#xff08;JVM&#xff09;的垃圾回收&#xff08;Garbage Collection, GC&#xff09;机制&#xff0c;是自动内存管理的重要组成部分。它通过回收不再使用的对象&#xff0c;避免手动释放内存的麻烦。然而&#xff0c;随着系统复杂性的增加&#xff0c…

4.C_数据结构_队列

概述 什么是队列&#xff1a; 队列是限定在两端进行插入操作和删除操作的线性表。具有先入先出(FIFO)的特点 相关名词&#xff1a; 队尾&#xff1a;写入数据的一段队头&#xff1a;读取数据的一段空队&#xff1a;队列中没有数据&#xff0c;队头指针 队尾指针满队&#…

劳特巴赫ICD调试器CMM调用烧录框架固件研究之C语言版本

接到客户一个项目是基本GD32F301C8XX的,尝试用手上的劳特巴赫仿真器对它进行开发操作,发现总是提示“FLASH algorithm did not execute completely” 怀疑是底层调用用烧录固件“~~/demo/arm/flash/word/stm32f300.bin”与芯片不兼容造成的,于是有了这编研究文档,多的不说直…

Spring4-IoC2-基于注解管理bean

目录 开启组件扫描 使用注解定义bean Autowired注入 场景一&#xff1a;属性注入 场景二&#xff1a;set注入 场景三&#xff1a;构造方法注入 场景四&#xff1a;形参注入 场景五&#xff1a;只有一个构造函数&#xff0c;无注解 场景六&#xff1a;Autowired和Quali…

Tcl lnit error: Can’t find a usable init.tcl in the following directories 问题解决

这个问题出现在我用py2exe打包了一个包含tkinter的图形化界面&#xff0c;在当前电脑上运行无问题&#xff0c;在移动到新电脑上后提示报错、 这里吐槽一下&#xff0c;新电脑上报错信息一闪而过&#xff0c;我用的土法子解决的&#xff0c;就是录视频然后0.25倍速度暂定找到报…

Acrobat 9 安装教程

软件介绍 Adobe Acrobat 是由Adobe公司开发的一款PDF&#xff08;Portable Document Format&#xff0c;便携式文档格式&#xff09;编辑软件。借助它&#xff0c;可以以PDF格式制作和保存文档&#xff0c;以便于浏览和打印&#xff0c;同时还可以使用一些高级工具来创建、编辑…

Qt 菜单栏、工具栏、状态栏、标签、铆接部件(浮动窗口) 设置窗口核心部件(文本编辑控件)的基本使用

效果 代码 #include "mainwindow.h" #include "ui_mainwindow.h" #include<QToolBar> #include<QDebug> #include<QPushButton> #include<QStatusBar> #include<QLabel> #include<QDockWidget> #include<QTextEdi…

将事物分为三教九流?不妨通过logistic回归

和多元线性回归一样&#xff0c;逻辑回归也是建立“多对一”型变量之间的线性关系——也即找出线性方程的近似解。有所不同的是&#xff0c;逻辑回归的解只能出现0~1之间&#xff08;亦或就是0/1两种结果&#xff09;&#xff0c;这倒是有点像bool型和int型之间的区别了。实际上…

S32K3 工具篇7:如何使用VScode编译EB MCAL工程

S32K3 工具篇7&#xff1a;如何使用VScode编译EB MCAL工程 1. VScode工具与配置2. 使用VScode编译RTD MCAL工程2.1 使用EB tresos生成配置2.2 VScode 打开工程2.3 修改mk文件2.4 编译文件2.5 debug生成好的elf文件 对于EB配置的MCAL代码&#xff0c;通常是基于RTD去做&#xff…

GEO IGEO MEO介绍 和 北斗导航系统使用三轨道原因

GEO IGSO MEO基本轨道知识 中地球轨道&#xff08;MEO&#xff1a;Middle Earth Orbit&#xff09; 轨道高度2000-36000kmGPS、GLONASS都属于此类轨道 地球同步轨道&#xff08;或称对地静止轨道&#xff09;[同步转动] 轨道高度约为36000 km&#xff1b;此轨道上卫星运行方…

情感识别系统源码分享

情感识别检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Vision …

发工资-python

题目要求&#xff1a; 代码&#xff1a; import random from random import randintmoney 10000 for i in range(1, 21):performance randint(1, 10)if performance < 5:print(f"员工{i},绩效分{performance},低于5,不发工资&#xff0c;下一位")continueif m…

每日学习一个数据结构-倒排表

文章目录 示意图倒排表的基本概念倒排表的数据结构示例 倒排表的优点应用场景 倒排表&#xff08;Inverted Index&#xff09;&#xff0c;也称为反向索引或倒排文件&#xff0c;在信息检索系统中是一种重要的数据结构。它主要用于快速搜索文档中的关键词&#xff0c;并找到包含…

字典+泛型的栈与队列+委托

字典 在System.Collections.Generic下&#xff0c;对应HashTable,添加了泛型的特性&#xff0c;性能更高更安全&#xff0c;在内存中散列排布&#xff0c;存储也是键值对。 Dictionary<键的数据类型&#xff0c;值的数据类型> 字典名new Dictionary<键的数据类型&am…

异常冲突行为和危险识别系统源码分享

异常冲突行为和危险识别检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Co…