datasets 数据处理封装后,统一处理流程以避免Dataset Map顺序依赖问题
文章目录
- 处理流程说明
- 小结
在实际项目中,我们常常需要对数据集进行预处理。为了规范操作,我封装了一个基础数据集处理类:
class DatasetAbstract:"""所有数据集都应包含以下几个字段:* question:用户提问文本* pos:与用户问题相关的正例文本* neg:与用户问题相关的负例文本* answer:用户问题对应的最终答案说明:部分数据集(如 HotpotQA、IIRC、2WikiMQA、Musique 等)本身就提供了 supporting facts,可用于支持正负例抽取。(参考 MDQA 论文)"""def __init__(self, dataset_name: str):self.dataset_name = dataset_nameself.dataset = self.load_dataset()# self.dataset = self.dataset.map(self.set_pos_text)# self.dataset = self.dataset.map(self.set_neg_text)# self.dataset = self.dataset.map(self.get_answer_data)# self.dataset = self.dataset.map(self.get_retrirver_data)# 数据集全部的处理过程都加载进内存,需要大内存self.dataset = self.dataset.map(self.full_process, num_proc=8)def full_process(self, item):item = self.set_pos_text(item)item = self.set_neg_text(item)item = self.get_answer_data(item)item = self.get_retrirver_data(item)return item
处理流程说明
在最初的设计中,我将数据处理函数(set_pos_text
、set_neg_text
、get_answer_data
、get_retrirver_data
)分步骤地进行 map
调用:
self.dataset = self.load_dataset()
self.dataset = self.dataset.map(self.set_pos_text)
self.dataset = self.dataset.map(self.set_neg_text)
self.dataset = self.dataset.map(self.get_answer_data)
self.dataset = self.dataset.map(self.get_retrirver_data)
这种分步 map
的方式在本地 Python 脚本运行时表现正常。但当在 Jupyter Notebook 中运行时,遇到了预期之外的问题,处理结果与脚本中的不一致。
经过排查,发现问题的根本原因在于 map
函数内部存在隐式的并行处理,尤其是在某些 datasets
库版本中,即便未显式指定 num_proc>1
,后台也可能开启轻量并发优化。这导致不同处理步骤之间的依赖关系被打破(如 set_neg_text
可能在 set_pos_text
之前被调用),进而产生错误结果。
因此,如果各处理步骤之间存在明显的先后依赖关系,推荐采用统一封装成一个大函数的方法,一次性进行完整处理,避免中间状态不一致的问题。
我最终将所有小处理函数整合到 full_process
中,确保各步骤按顺序执行,并在一次 map
中完成,成功得到了预期结果。
小结
总结经验:
- 如果数据处理函数之间没有强依赖,可以分步 map,提高可读性和模块化程度。
- 如果函数之间有顺序依赖,务必封装成一个统一的处理函数,确保数据流正确。
- 注意不同运行环境(如本地脚本、Jupyter Notebook、服务器环境)下
datasets.map()
的并行行为可能存在差异。 - 若需要明确控制,可以设置
num_proc=1
强制单进程,避免并发问题。