来源:市场资讯

(来源:51CTO技术栈)

作者 |
打开网易新闻 查看精彩图片
作者 |

DataFlow / DataFlex 技术团队

做过大模型微调的人,多多少少都经历过这样一个场景:手头有一堆业务文档,比如 PDF 格式的产品手册、行业报告,从内部系统导出的文本数据等等。

任务目标是用这些数据微调一个领域模型。听起来简单,但真正动手的时候会发现,准备数据的工作量远超跑训练本身。

既然痛点在数据,顺着这个思路往下,本文就聊聊怎么从原始文件出发,经过数据准备、动态训练,完成最终的模型训练。这中间涉及三个开源项目——DataFlow(数据生产)、LlamaFactory(模型训练)、DataFlex(数据驱动的进阶训练),看看它们各自解决什么问题、如何协作、背后的设计思路是什么。

痛点:清洗、对齐全靠写脚本,现有工具只能解决单点问题

大模型训练框架虽然已经非常成熟,LlamaFactory、Axolotl 这些项目,SFT / LoRA / 全参微调都做得很好用,配置一个 YAML 就能跑起来。但问题往往出在训练之前。一个典型的微调项目需要经历解析原始文件、清洗切分、生成训练样本、格式对齐以及数据合成等繁琐步骤。这些步骤之间缺乏统一的工具链,开发者通常得自己写一堆脚本,每换一个数据源就重来一遍。更麻烦的是,在真实项目中这些步骤并非线性推进,而是一个需要不断重复调整的回流闭环。

即使费尽心力做好了数据,也不意味着能直接喂给训练框架。不同框架对数据格式有着严格且差异化的要求,比如 LlamaFactory 的 Alpaca 格式需要特定的三个字段,ShareGPT 格式要求特定的列表结构,CoT 推理数据还需要特定标签包裹。这些格式细节一旦出错,就会直接导致训练时的 loss 计算异常,而且这种错误往往非常隐蔽,极难排查。

更棘手的问题出现在多数据源混合的场景下。当医疗、法律、通用等不同领域的数据拼接在一起时,本质上是在混合多种分布不同的数据,它们在文本风格、信息密度和问答结构上都存在巨大差异。不同来源的数据该如何组织和配比缺乏明确的判断标准,不存在一个显式的最优比例,开发者通常只能通过反复实验去逼近,这使得数据准备不仅是繁重的工程问题,更带有极高的经验依赖和试错成本。

仔细拆解这三个痛点,会发现问题其实并不出在单个环节上。市面上的开源工具大多是为了解决特定问题而生,一旦要拼装成一条完整链路,脱节就不可避免:

  • 数据端:Nemo-Curator、Data-Juicer 等项目专注于数据清洗和过滤,但大多面向预训练语料的质量筛选,对"从文档生成 SFT 训练样本"这个需求的支持比较有限。想直接完成"PDF → 问答对 → Alpaca JSON"这样的流程,基本做不到。

  • 训练端:LlamaFactory 等框架在训练侧已经足够强大,但它们的前提是数据已经准备好了。框架本身不考虑从原始文档到训练数据之间的 gap 问题。

  • 数据策略端:DoReMi、LESS 这些数据选择和混合算法,在论文里效果很好,但要落地到实际训练流程中,需要修改训练器的 loss 计算、数据加载、采样逻辑,工程门槛不低。

最终,开发者只能靠写胶水代码把这些环节强行拼凑在一起,根本形不成一条顺畅的链路。

完整解决路径:从原始数据,到动态模型训练

针对上述这些脱节的环节,DataFlow、LlamaFactory 和 DataFlex 组合起来,就是一条从原始文件到微调模型的完整、标准化路径。

打开网易新闻 查看精彩图片

三个项目的分工非常清晰:

  • DataFlow解决"数据怎么造"——把各种原始文件变成模型能学的训练样本

  • LlamaFactory解决"模型怎么练"——标准的 SFT / LoRA 微调

  • DataFlex解决"数据怎么用更好"——在训练过程中智能调度数据

它们之间的衔接点是标准的 JSON 数据格式,各环节可以独立使用,也可以组合使用。

01 DataFlow:把数据准备变成可编排的流水线

DataFlow 的核心思路是把数据准备从"写一堆临时脚本"变成"组装一条可复用的流水线"。

它采用 算子(Operator) 设计——每个处理步骤被封装为一个独立的算子,有明确的输入输出契约。算子之间通过统一的存储层传递数据,上游的输出自动成为下游的输入。这样做的好处是:替换其中某个环节,比如换一种切分策略,或者用不同的 LLM 生成 QA,只需要替换对应的算子,不影响流水线的其他部分。

打开网易新闻 查看精彩图片

为了方便使用,DataFlow 也内置了多种面向不同任务的流水线。比如:

  • 文本处理流水线:从纯文本中挖掘问答对,用于 SFT 训练

  • 知识库清洗流水线:从 PDF、Word 等文档中提取结构化知识

  • 推理流水线:为已有问答对增强 Chain-of-Thought 推理链

  • Agentic RAG 流水线:生成需要外部工具调用才能回答的训练数据

  • Text2SQL 流水线:将自然语言问题转化为 SQL 查询

不同类型的输入会触发不同的处理路径。当输入是 PDF 时,流水线会先调用文档解析(如 MinerU)将 PDF 转为结构化文本;当输入已经是干净的文本时,这一步直接跳过。DataFlow 的价值在这里体现——它能根据数据来源和目标任务,动态组合合适的处理路径,而不是绑死在某一个固定组件上。

除了使用内置流水线之外,DataFlow 还支持通过 DataFlow-WebUI 以拖拽方式自定义流水线——在可视化界面中选择算子、连接数据流、调整参数,然后直接运行。对于内置流水线无法覆盖的业务场景,比如特定领域的数据增强逻辑、或者需要串联多种异构数据源,可以在 WebUI 中自由组合已有算子,甚至加入自定义算子,搭建出完全贴合需求的处理流程。这把"编排流水线"这件事从写代码变成了画流程图,上手门槛大幅降低。

在此之上,DataFlow 最新引入 DataFlow-Skills:通过结构化 Skills,把 DataFlow 的抽象能力交给 Agent(如 Claude Code、Cursor)。开发者只需用自然语言描述数据处理目标,Agent 就可以理解这些抽象,并自动生成对应的数据算子与完整流水线,实现从“手动编排”到“自然语言生成流水线”的进一步自动化。

一个典型流程:文本 / PDF → 训练数据

以最常见的场景为例——拿到一批文本或 PDF 数据,需要生成可用于 SFT 训练的问答对。DataFlow 的处理分四步:

第一步:文本切分。长文本被按照指定策略(按 token 数、按语义边界等)切成多个 chunk。这一步的目的是让后续的 LLM 处理能够在上下文窗口内完成,同时保证每个 chunk 包含足够完整的语义信息。

第二步:知识清洗。每个 chunk 经过 LLM 清洗,去除广告、导航信息、格式噪声等无关内容,只保留有效的知识内容。这一步的设计比较巧妙——LLM 返回的结果用特定标签( / )包裹,算子自动提取标签间的内容。如果 LLM 返回格式异常,也有兜底逻辑直接使用原文。

第三步:QA 生成。基于清洗后的文本,LLM 生成结构化的多跳问答对。每个 QA 对不仅包含问题和答案,还包含推理步骤(reasoning_steps)、支撑事实(supporting_facts)和领域标签。这些结构化信息在后续的格式转换中可以灵活使用——比如把推理步骤转为 CoT 训练数据。

第四步:格式化输出。将 QA 数据转为 LlamaFactory 可直接使用的 Alpaca 格式(instruction / input / output),同时自动生成 dataset_info.json 配置文件。

如果只讲抽象流程,这里还是有点空。DataFlow 仓库里其实就有一个很贴近真实使用方式的 PDF 案例:知识库清洗流水线 KBCleaningPipeline。它的示例输入文件 kbc_test_1.jsonl 里直接放了一份 PDF 路径和若干 URL,其中第一条就是:

{"source": "../example_data/KBCleaningPipeline/bitter_lesson.pdf"}

这条流水线在代码里是明确串起来的:先把 PDF 转成 Markdown/文本,再切成raw_chunk,随后清洗成cleaned_chunk最后生成 QA_pairs 和 QA_metadata。默认配置里,切分算子使用 token 级切分、chunk_size=512,QA 生成阶段一次会为每个清洗后的 chunk 生成多条问题。换句话说,输入不是直接“整本 PDF 喂给模型”,而是先变成一批更短、更干净、适合生成训练样本的知识片段。

如果把这个过程写成一个最小化的中间产物,大致会长这样:

{"raw_chunk": "The bitter lesson is based on the historical observations that ...","cleaned_chunk": "The Bitter Lesson argues that methods leveraging computation at scale usually outperform systems heavily dependent on human-crafted knowledge.","QA_pairs": {"qa_pairs": ["question": "What is the core argument of The Bitter Lesson?","answer": "Its core argument is that scalable methods based on search and learning tend to beat approaches that rely on handcrafted domain knowledge.","reasoning_steps": ["Locate the thesis statement in the cleaned chunk","Identify the contrast between scalable computation and handcrafted knowledge","Rewrite it as a direct answer"],"supporting_facts": ["The text compares search/learning methods with hand-designed heuristics"}

DataFlow 知识库清洗流水线和 QAExtractor 的实际输出组织方式为:上游产出 QA_pairs,下游再把其中的 question / answer 提取出来,转换成 LlamaFactory 能直接消费的 Alpaca 格式。转换后的样本会收敛成这样的结构:

{"instruction": "Please answer the following question based on the provided information.","input": "What is the core argument of The Bitter Lesson?","output": "Its core argument is that scalable methods based on search and learning tend to beat approaches that rely on handcrafted domain knowledge."}

与此同时,pdf2model 的 CLI 还会自动生成对应的 dataset_info.json,把这份 qa.json 注册成一个 Alpaca 数据集,字段映射也一并写好:

{"pdf_kbc_dataset": {"file_name": "qa.json","formatting": "alpaca","columns": {"prompt": "instruction","query": "input","response": "output"}

这样就能看见一条比较完整的证据链:输入端是一份真实 PDF;中间不是黑盒,而是会落出 raw_chunk、cleaned_chunk 和结构化的 QA_pairs;输出端则是标准的 qa.json + dataset_info.json,可以直接接到 LlamaFactory。它未必保证每条 QA 都足够好,但至少把“原始文档如何变成训练样本”这件事落成了可检查、可复用的中间产物,而不只是停留在口头描述上。

整个流程通过 CLI 一键完成:

# 初始化项目dataflow text2model init# 放入数据,启动全流程dataflow text2model train

如果输入是 PDF,换用 pdf2model 子命令即可,DataFlow 会自动在流水线前端加入文档解析环节,并在训练前检查 qa.json 与 dataset_info.json 是否已经生成、字段是否符合 Alpaca 或 ShareGPT 预期。

为什么不是"又一个 ETL 工具"

DataFlow 和传统 ETL 工具的核心区别在于:它用 LLM 来生成训练数据,而非仅仅做数据搬运和清洗。文本切分和清洗只是前置工序,真正产出价值的是 QA 生成和格式化这两步——它把非结构化的知识变成了模型可以学习的结构化训练信号。

而且,由于采用算子化设计,整条流水线是可定制的。DataFlow 初始化时会把流水线脚本复制到工作目录,可以直接修改——换 LLM、调 chunk 大小、加自定义的后处理逻辑,都不需要动框架本身。更偏好图形化操作的话,也可以直接在 WebUI 上完成这些调整。

02 LlamaFactory:拿到数据之后怎么训

DataFlow 输出的是标准 Alpaca / ShareGPT 格式的 JSON 文件,加上一份 dataset_info.json 字段映射——这就是 LlamaFactory 所需要的全部输入,两者之间没有 API 调用、没有服务依赖,纯粹靠数据格式约定衔接。LlamaFactory 是目前社区最广泛使用的 LLM 微调框架之一,支持几乎所有主流模型,并且把训练流程简化到了"写一个 YAML + 一行命令"的程度。

训练配置

DataFlow 在初始化时会自动生成一份训练配置(.cache/train_config.yaml),覆盖了模型选择、微调方法、数据集指向、训练超参等所有必要项,训练前可以根据需要调整:

stage: sft                              # 训练阶段model_name_or_path: Qwen/Qwen2.5-7B-Instruct  # 基座模型template: qwen                          # 对话模板finetuning_type: lora                   # 微调方法lora_rank: 16                           # LoRA 秩dataset: kb_qa                          # 数据集名称(对应 dataset_info.json)dataset_dir: ./.cache/data              # 数据目录learning_rate: 5e-5                     # 学习率num_train_epochs: 2.0                   # 训练轮数bf16: true                              # 使用 BF16 精度

内部是怎么跑的

LlamaFactory 的 SFT 训练流程:

读取 dataset_info.json,找到数据文件和字段映射

用对应的 Converter(Alpaca 或 ShareGPT)将原始数据转为统一的消息格式

用 Template 将消息编码为 token ID,并生成 label(prompt 部分标记为不计算 loss,response 部分正常计算)

送入 Trainer 开始训练

其中 Template 这一层是 LlamaFactory 的一个亮点设计——不同模型的对话模板差异很大(Qwen 和 LLaMA 的 special token、角色标记都不一样),Template 层把这些差异统一封装了。配置里写 template: qwen,框架就会自动处理所有格式细节。

启动训练

# 方式一:通过 DataFlow 一键执行(数据准备 + 训练)dataflow text2model train# 方式二:手动调用 LlamaFactoryllamafactory-cli train .cache/train_config.yaml# 训练完成后测试dataflow chat

03 DataFlex:当"训什么数据"也需要优化

其实,单靠 DataFlow 造数据、LlamaFactory 训模型,已经能跑通绝大多数常规微调场景,构成了一个基础闭环。DataFlex 要解决的,是更进阶的难题。比如同时有医疗、法律、金融三个领域的数据,简单混在一起训效果不好,手动调比例又是个黑盒,这时候就需要 DataFlex。

核心理念:让训练过程"感知"数据

DataFlex 构建在 LlamaFactory 之上,它的核心理念是把数据调度从"训练前的静态决策"变成"训练中的动态优化"。

打开网易新闻 查看精彩图片

具体来说,DataFlex 提供了三种策略:

动态数据选择(Dynamic Select):不是所有数据都同等重要。有些样本是"困难样本",模型还没学会;有些是"简单样本",模型已经掌握了。动态选择策略在训练过程中评估每个样本的价值,优先让模型学习更有帮助的数据。内置了 LESS、NICE、Delta Loss 等算法

动态数据混合(Dynamic Mix):多数据源场景下,各领域的最优配比并不是固定的——训练初期可能需要更多通用数据打基础,后期需要更多领域数据精调。动态混合策略(如 DoReMi)在训练过程中周期性地重新计算各领域的最优配比,自动调整数据比例。

动态样本加权(Dynamic Weight):在反向传播时,给不同样本施加不同的损失权重。效果上类似于"让模型更关注某些数据",但粒度更细,直接作用在 loss 计算上。

怎么做到的:运行时注入

DataFlex 有一个很实用的设计决策——不需要 fork 或修改 LlamaFactory 的代码。

它的做法是在启动训练前,通过 Python 的模块系统在运行时替换 LlamaFactory 的关键组件。具体来说,它替换了三个东西:

参数类:扩展 LlamaFactory 的训练参数,加入 DataFlex 特有的配置项(如 train_type、warmup_step、update_step 等)

训练器:根据配置中指定的策略,将 LlamaFactory 的默认 Trainer 替换为 DataFlex 的自定义 Trainer(SelectTrainer / MixTrainer / WeightTrainer)

数据加载器:在动态混合模式下,替换数据加载逻辑以支持多数据源的动态采样

替换完成后,直接调用 LlamaFactory 原有的训练入口 run_exp()。这意味着所有 LlamaFactory 支持的模型、微调方法、分布式训练策略,DataFlex 都天然支持。

动态混合是怎么跑的

以最有代表性的动态混合(DoReMi)为例,训练过程是这样的:

预热阶段:先用初始比例跑若干步,让模型有一个基础能力

评估比例:预热结束后,混合算法根据模型在各领域数据上的表现(loss 差异),计算新的最优比例

重建数据集:按新比例重新采样数据,创建新的 DataLoader

继续训练:用新比例的数据继续训练若干步

重复 2-4:周期性地更新比例,直到训练结束

在这个过程中,有一个关键的技术细节:每个样本都被打上了 domain_id 标签,标识它来自哪个数据源。这个标签在数据加载时注入,在 loss 计算时使用——MixTrainer 会根据各域的权重对 loss 进行加权,确保模型的优化方向与期望的数据分配一致。

如果只讲机制,DataFlex 还是容易显得像“理念正确”。它的 README 里给了一组更具体的实验结果。以数据配比实验为例,作者在 SlimPajama-627B 的子集上比较了基线训练、DoReMi 和 ODM 两种动态混合方法。在 Slim-Pajama-6B 设置下,基线的 MMLU 是 25.27,DoReMi 提升到 25.84,ODM 提升到 26.04;同时 DoReMi 的整体 PPL 从 4.217 降到 4.134。在 Slim-Pajama-30B 设置下,基线 MMLU 为 25.51,DoReMi 提升到 25.97,而 ODM 把整体 PPL 从 3.584 降到 3.429。幅度不算夸张,但它至少说明了一点:动态混合带来的不是抽象上的“更智能”,而是在公开实验里能观测到的准确率提升和困惑度下降。

数据选择和动态加权那一组实验也传达了类似信号:在 Open-Hermes-2.5 子集上,相比 random selector,LESS、NICE 以及重加权方法在相关 MMLU 子集上都有更好的表现。也就是说,DataFlex 的价值不只是“把论文算法接进训练器”,而是把这些原本需要深改训练框架的策略,变成了可以在 LlamaFactory 工作流里直接复用的实验能力。

使用

DataFlex 的使用和 LlamaFactory 几乎一样,只是命令从 llamafactory-cli 换成 dataflex-cli,YAML 配置中多了动态训练相关的参数:

# 标准 LlamaFactory 参数model_name_or_path: Qwen/Qwen2.5-7B-Instructstage: sftdataset: medical_data,legal_data,general_datatemplate: qwenfinetuning_type: lora# DataFlex 动态训练参数train_type: dynamic_mix                  # 策略:dynamic_select / dynamic_mix / dynamic_weightcomponent_name: doremi                   # 算法init_mixture_proportions: [0.3, 0.3, 0.4]  # 初始比例warmup_step: 100update_step: 200update_times: 5

dataflex-cli train config.yaml

小结

回到最开始的问题,从一堆 PDF 到跑起微调模型,中间卡住的往往是各环节之间的接口。DataFlow、LlamaFactory 和 DataFlex 这三个项目没有互相深度绑定,全靠一份标准的 JSON 格式把断掉的环节接上。

拿 DataFlow 洗出来的数据,可以直接扔给 LlamaFactory,换别的训练框架也行;想用 DataFlex 调数据比例,同样只需要改改配置。各个环节能单独拆出来用,也能拼在一起跑。

当微调前的数据处理,从写一堆一次性脚本,变成能检查中间产物、随时替换算子的流水线时,前文提到的那些痛点才算有了真正的工程解法。