- 人工智能
- 大模型
- 数据工程
- 数据清洗
- 数据增强
- 数据质检
【免费下载链接】data-juicer
Data processing for and with foundation models! 🍎 🍋 🌽 ➡️ ➡️🍸 🍹 🍷
导读
sentence_split_mapper是 Data-Juicer 中用于将文本样本按指定语言拆分为独立句子的文本处理算子(mapper)。它以 NLTK 的 punkt 无监督分词模型为底层引擎,支持英语、法语、葡萄牙语、西班牙语等语言的句子切分,并通过批次(batched)处理提升数据流水线的吞吐效率。读完本文后,你将掌握该算子的参数语义、语言支持范围、底层 NLTK 模型加载机制、批处理实现原理,以及如何在 Data-Juicer 的 YAML 配置与 Python API 中直接落地使用它。
算子概览:把整段文本切分成句子列表
在数据清洗与语料预处理流程中,许多下游任务(如逐句去重、句子级质量过滤、句子级翻译对齐、RAG 切块前的边界判定)都需要先把整篇文档拆成独立句子。sentence_split_mapper正是为此设计:它接收每个样本text_key字段的整段文本,用基于语言感知的分词器将其拆分成句子,并用换行符\n重新拼接后写回原字段。
官方文档对它的核心定义是:Splits text samples into individual sentences based on the specified language——即“根据指定语言将文本样本拆分成单独句子”。该算子使用基于 NLTK 的分词器,语言在初始化时指定;每个样本的原始文本被替换为句子列表(实际为以\n连接的句子串)。为提升效率,算子以批次方式处理样本。
- 算子类型(Type):mapper
- 标签(Tags):cpu, text——纯 CPU 计算,作用于文本字段,不依赖 GPU 资源
- 注册名(OP_NAME):
sentence_split_mapper,见 算子源码 中的OP_NAME = "sentence_split_mapper",并经由@OPERATORS.register_module(OP_NAME)注册进全局算子注册表
参数配置:lang 决定分词语言
官方文档给出的参数表如下:
| 参数名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
lang | str | 'en' | split sentence of text in which language(按哪种语言拆分句子) |
args | — | '' | extra args(透传给基类的额外参数) |
kwargs | — | '' | extra args(透传给基类的额外关键字参数) |
核心参数只有一个lang。从 model_utils.py 的prepare_nltk_model实现可以看到,当前版本通过nltk_to_punkt字典把语言代码映射到 NLTK punkt 模型的资源名:
nltk_to_punkt = {"en": "english", "fr": "french", "pt": "portuguese", "es": "spanish"} assert lang in nltk_to_punkt.keys(), "lang must be one of the following: {}".format(list(nltk_to_punkt.keys()))也就是说,当前仓库支持的lang取值严格限定为en、fr、pt、es四种,对应英语、法语、葡萄牙语、西班牙语;传入其他语言代码会在初始化阶段直接抛出断言错误。官方文档强调:“请确保将lang参数设置为适当的语言代码(例如'en'表示英语),以实现准确的句子拆分。”
底层加载的资源路径为tokenizers/punkt/{english|french|portuguese|spanish}.pickle,即 NLTK 自带的 punkt 分词模型(一种无监督的句子边界检测模型,通过标点、缩写词与上下文特征识别句子边界)。
底层原理:从lang到 NLTK punkt 模型的完整调用链
1. 初始化:准备模型而非立即加载
在 SentenceSplitMapper.init中,算子做了三件事:
- 调用基类
super().__init__(*args, **kwargs)完成通用 Mapper 初始化; - 保存
self.lang = lang; - 调用
patch_nltk_pickle_security()修复 NLTK 的 pickle 安全限制(见下文),再通过prepare_model(model_type="nltk", lang=lang)准备模型。
prepare_model并不是立即加载模型,而是返回一个绑定了参数的偏函数(partial)作为model_key。在 model_utils.py 中可以看到:
def prepare_model(model_type, **model_kwargs): assert model_type in MODEL_FUNCTION_MAPPING.keys(), ... model_func = MODEL_FUNCTION_MAPPING[model_type] model_key = partial(model_func, **model_kwargs) if model_type in _MODELS_WITHOUT_FILE_LOCK: # initialize once in the main process to safely download model files model_key() return model_key模型注册表MODEL_FUNCTION_MAPPING中"nltk"对应prepare_nltk_model;同时nltk被列入_MODELS_WITHOUT_FILE_LOCK集合,意味着它会在主进程中被提前初始化一次,从而安全地完成模型文件下载(避免多进程并发下载冲突)。
2. 模型准备:资源检查 + 自动下载
prepare_nltk_model的核心流程(model_utils.py):
- 再次调用
patch_nltk_pickle_security(),确保安全补丁已应用; - 校验
lang是否在nltk_to_punkt映射中; - 计算资源路径
tokenizers/punkt/{nltk_to_punkt[lang]}.pickle; - 调用
ensure_nltk_resource(resource_path, "punkt")检查资源,若缺失则触发nltk.download("punkt")自动下载; - 最后通过
nltk.data.load(resource_path)加载 punkt 模型并返回。
ensure_nltk_resource(定义于 nltk_utils.py)做了更健壮的容错:它先处理若干已知的 NLTK 数据路径兼容问题(path_mappings),然后主动下载punkt包到 NLTK 默认数据目录,再尝试nltk.data.find定位资源;如果仍未找到,还会做二次下载尝试。这意味着首次使用该算子时无需手动安装 NLTK 数据,算子会自动完成资源准备。
3. pickle 安全补丁:兼容 NLTK 3.9+
NLTK 3.9+ 引入了严格的 pickle 安全限制(restricted_pickle_load),会阻止加载部分旧版模型文件。patch_nltk_pickle_security()(nltk_utils.py)通过替换nltk.data.restricted_pickle_load为更宽松的加载器来绕过该限制,同时保持安全机制。该函数应在任何 NLTK 调用前于初始化阶段执行一次——这正是算子在__init__中首先调用它的原因。这解释了为什么该算子在较新版本的 NLTK 环境中依然能顺利加载 punkt 模型。
4. 批处理:process_batched的实现
该算子声明了_batched_op = True,并实现process_batched(sentence_split_mapper.py):
def process_batched(self, samples): # Get the sentence tokenizer model nltk_model = get_model(self.model_key) samples[self.text_key] = [ get_sentences_from_document(text, model_func=nltk_model.tokenize if nltk_model else None) for text in samples[self.text_key] ] return samplesget_model从模型缓存池MODEL_ZOO中取出(或惰性创建)语言分词模型,避免每个批次重复加载;在非主进程中它还会调用setup_worker_threads(num_threads=1)以避免多进程场景下的线程过度订阅;- 对
samples[self.text_key]中的每一条文本调用get_sentences_from_document; - 最终覆盖写回原字段:
text字段中的整段文本被替换为以\n连接的句子序列。
get_sentences_from_document定义于 helper_func.py:
def get_sentences_from_document(document, model_func=None): if model_func: sentences = model_func(document) else: sentences = document.splitlines() return "\n".join(sentences)可以看到一个关键设计:当传入model_func(即 punkt 模型的tokenize方法)时,使用模型做真正的语义级句子切分;若模型缺失(model_func=None),则退化为朴素的splitlines()按行切分。因此,是否加载成功 punkt 模型直接决定了切分质量——这也是文档强调正确设置lang的原因所在。
效果演示:多语言句子拆分实测
以下示例完整摘自官方文档与对应单元测试,可在本地直接复现。
英语(lang='en')
SentenceSplitMapper('en')输入数据(Sample 1: text)
Smithfield employs 3,700 people at its plant in Sioux Falls, South Dakota. The plant slaughters 19,500 pigs a day — 5 percent of U.S. pork.输出数据(Sample 1: text)
Smithfield employs 3,700 people at its plant in Sioux Falls, South Dakota. The plant slaughters 19,500 pigs a day — 5 percent of U.S. pork.解释:算子将英文文本拆分为独立句子并在句子间插入换行符(\n)。输入包含两个句子——一句关于雇用人数,一句关于每日屠宰猪的数量;输出中两句话被换行分隔,清晰表明文本已被切分为组成句。
法语(lang='fr')
SentenceSplitMapper('fr')输入数据(Sample 1: text)
Smithfield emploie 3,700 personnes dans son usine de Sioux Falls, dans le Dakota du Sud. L'usine abat 19 500 porcs par jour, soit 5 % du porc américain.输出数据(Sample 1: text)
Smithfield emploie 3,700 personnes dans son usine de Sioux Falls, dans le Dakota du Sud. L'usine abat 19 500 porcs par jour, soit 5 % du porc américain.解释:算子将法语文本拆分为独立句子并插入换行符。值得注意的是,法语文本中包含3,700(逗号作千分位)与L'usine(撇号缩约)等易混淆的边界信号,punkt 模型均能正确识别句子边界而不误切。
更多语言:葡萄牙语与西班牙语
虽然官方效果演示只给出en与fr两个案例,但仓库的 单元测试 进一步覆盖了pt与es,且与en/fr一样都通过了断言验证(assertEqual(data['text'], data['target'])):
- 葡萄牙语:
SentenceSplitMapper('pt'),输入A Smithfield emprega 3.700 pessoas em sua fábrica em Sioux Falls, Dakota do Sul. A fábrica abate 19.500 porcos por dia – 5% da carne suína dos EUA.,输出将两个句子以\n分隔。此处葡萄牙语使用点号(.)作千分位(3.700、19.500),punkt 模型同样不会将其误判为句号; - 西班牙语:
SentenceSplitMapper('es'),输入Smithfield emplea a 3.700 personas en su planta de Sioux Falls, Dakota del Sur. La planta sacrifica 19.500 cerdos al día, el 5 por ciento de la carne de cerdo de EE.,输出同理。
这四个测试用例共同验证了:en/fr/pt/es四种语言均可正确完成句子切分,且对不同语言的数字分隔符、缩写词习惯具备鲁棒性。
实际使用:YAML 配置与 Python API 两种接入方式
方式一:在数据处理配置文件中声明(推荐)
在 Data-Juicer 的 YAML 配置中,将sentence_split_mapper加入process列表即可。以英文语料为例:
process: - sentence_split_mapper: lang: 'en'lang不写时默认取'en'。法语、葡语、西语语料分别将lang改为'fr'、'pt'、'es'。配置文件的全局字段(如dataset_path、text_key等)与该算子无关,算子只处理text_key指定的文本字段。
方式二:在 Python 代码中直接调用
参照 test_sentence_split_mapper.py 的写法,可以像这样在脚本中单独使用该算子:
from data_juicer.core.data import NestedDataset as Dataset from data_juicer.ops.mapper.sentence_split_mapper import SentenceSplitMapper samples = [ { 'text': 'Smithfield employs 3,700 people at its plant in Sioux Falls, ' 'South Dakota. The plant slaughters 19,500 pigs a day — 5 ' 'percent of U.S. pork.', }, ] dataset = Dataset.from_list(samples) op = SentenceSplitMapper('en') dataset = dataset.map(op.process, batch_size=2) for data in dataset: print(data['text'])输出将得到以\n分隔的两句话。这里的batch_size=2与算子_batched_op = True的属性相呼应:算子以批次为粒度处理多个样本,而非逐样本处理,从而在大规模语料上获得更高的吞吐。若更换语言,只需SentenceSplitMapper('fr')、SentenceSplitMapper('pt')、SentenceSplitMapper('es')。
运行前提与注意事项
- NLTK 依赖:该算子依赖
nltk库及其punkt数据包。首次使用时会自动下载,但离线或受限网络环境下需提前手动准备nltk_data;下载与资源定位由ensure_nltk_resource统一处理(见 nltk_utils.py)。 - 语言范围受限:
lang仅接受en、fr、pt、es,这是由prepare_nltk_model中的nltk_to_punkt映射与断言决定的,其他语言会初始化失败。如需支持更多语言,需自行扩展该映射并准备对应 punkt 资源。 - 输出为换行连接的字符串:尽管文档描述为“句子列表”,实际写回
text_key字段的是用\n连接句子后的单个字符串(get_sentences_from_document的"\n".join(sentences)),而非 Python 的 list 对象——在断言下游处理逻辑时需留意这一细节。 - 覆盖写回:算子直接覆盖原文本字段,若需保留原文,应在配置中为其配置独立的文本列,或使用带文本副本的预处理步骤。
- 纯 CPU 运行:标签为
cpu, text,不涉及 GPU 或多模态资源,适合作为流水线早期的文本规范化步骤。
延伸阅读
- 算子源代码:完整的注册、初始化与批处理实现
- 单元测试:
en/fr/pt/es四种语言的断言用例 - NLTK 模型准备与安全补丁:
prepare_nltk_model、prepare_model、get_model的实现 - 句子切分公共函数:
get_sentences_from_document的模型回退逻辑 - NLTK 资源工具:
ensure_nltk_resource与patch_nltk_pickle_security - 返回算子列表:Data-Juicer 全部算子索引,可按类型与标签检索更多 mapper/filter 算子
- 人工智能
- 大模型
- 数据工程
- 数据清洗
- 数据增强
- 数据质检
【免费下载链接】data-juicer
Data processing for and with foundation models! 🍎 🍋 🌽 ➡️ ➡️🍸 🍹 🍷
相关推荐
Data-Juicer 句子增强算子 sentence_augmentation_mapper 实战指南:基于 Hugging Face 大模型的句子级数据增强
Data Juicer 句子增强算子 sentence_augmentation_mapper 实战指南:基于 Hugging Face 大模型的句子级数据增强
人工智能大模型数据工程数据清洗数据增强数据质检Data-Juicer LLMDifficultyScoreFilter 算子详解:基于 LLM 多维评分的样本难度筛选
Data Juicer LLMDifficultyScoreFilter 算子详解:基于 LLM 多维评分的样本难度筛选 导读 本文围绕 Data Juicer
人工智能大模型数据工程数据清洗数据增强数据质检Data-Juicer naive_reverse_grouper 算子详解:批量样本拆解与 batch_meta 导出实战
Data Juicer naive_reverse_grouper 算子详解:批量样本拆解与 batch_meta 导出实战 naive_reverse_gro
人工智能大模型数据工程数据清洗数据增强数据质检
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考