Open-Assistant 数据集工程实践:SODA Synthetic Dialogue 合成对话数据集的构建、加载与训练接入指南
【免费下载链接】Open-AssistantOpenAssistant is a chat-based assistant that understands tasks, can interact with third-party systems, and retrieve information dynamically to do so.项目地址: https://gitcode.com/gh_mirrors/op/Open-Assistant
🥤SODA Synthetic Dialogue 是 Open-Assistant 仓库中一个用于对话生成(dialogue-generation)任务的大规模合成数据集,它从 SODA 原始故事对话数据出发,通过模板化改写生成 150 万条以上的「User–Assistant」多轮对话,覆盖摘要、故事续写、标题生成与主题抽取等指令形态。本文将基于 数据集卡片 的完整说明,结合仓库内的生成脚本、Hugging Face 加载脚本与模型训练端代码,逐步拆解该数据集的构建原理、数据结构、加载方式以及接入 SFT 训练链路的具体方法,读完即可在本地复现生成流程并直接复用它训练对话模型。
一、数据集概览:它是什么、解决什么问题
SODA Synthetic Dialogue 是一组在Assistant与User之间展开的合成对话。每条对话中,User 会基于一段已有的对话片段、故事,或者一个标题/主题,向 Assistant 提出摘要、故事续写或故事生成类任务。其核心价值在于:把原本"故事叙述体"的 SODA 语料,通过一套模板系统转化为"指令-回答"形态的对话语料,从而更适合用来训练具备指令跟随能力的对话式语言模型。
根据数据集卡片 frontmatter 中的元信息,该数据集的客观属性如下:
| 属性 | 取值 |
|---|---|
| pretty_name | SODA Synthetic Dialogue |
| annotations_creators | no-annotation(无人工标注,机器生成) |
| language / multilinguality | en / monolingual(纯英文) |
| license | mit |
| size_categories | 1M < n < 10M |
| source_datasets | extended | allenai/soda |
| task_categories / task_ids | conversational / dialogue-generation |
| config_name | soda_synthetic_dialogue_dialogue_modeling |
三个官方切分的规模(来自卡片 frontmatter):
| split | num_examples | num_bytes |
|---|---|---|
| train | 1,191,582 | 1,524,941,476 |
| test | 148,968 | 190,565,707 |
| validation | 146,346 | 187,161,878 |
数据集整体下载体积约 1.95 GB(download_size),展开后约 1.9 GB(dataset_size)。
数据集由 ontocord 原创合成思路(最初以 Colab notebook 形式公开),后由 Jeffrey Quesnelle 整理为 Hugging Face 数据集格式。其生成依据是 SODA 原始研究论文(arXiv:2212.10465)。
二、数据形态与典型样本解读
数据集卡片给出了一个完整示例。这条样本对应"场景续写 + 标题 + 摘要 + 主题"四段式交互,完整还原了模板化合成的输出形态:
User: Can you write the next few lines of dialogue for this scene: Cornell: I know what you're thinking, Dontrell. I can sense your feelings and know exactly what you're going to say or do next. You're thinking about how much you miss your family, and how you wish you could see them again. You're wondering if there's anything you can do to make that happen. And you're also feeling a little guilty, because you know that if you could see them again, it would only be for a short while before they died. Dontrell: Cornell, how can you possibly know all that? It's like you can read my mind! Assistant: Sure, the next dialogue for this scene could be: Cornell: I told you, I can sense your feelings. It's like I have a connection to your thoughts and emotions. I'm not sure why or how it works, but ever since we met, I've been able to understand what you're thinking and feeling pretty easily. Dontrell: Wow...that's amazing! And a little bit scary too. But it does explain why we've always been so close - even though we've only known each other for a few months. Cornell: Yeah, I guess our connection is pretty strong. But don't worry, Dontrell - I'll never use it to manipulate or control you in any way. I promise." Dontrell: That's a relief! I was starting to feel a little bit like you could read my mind, and it was kind of creepy. But if you promise not to use your abilities to control me, then I'm okay with it. Cornell: I swear, Dontrell. I'll never use my abilities to control or manipulate you in any way. You have my word. User: And a title? Assistant: Sure, a title for this dialogue could be: Cornell is intuitive. Cornell reads Dontrell's mind. User: How about a short description? Assistant: Sure, a short description for this dialogue could be: Cornell knows what Dontrell is thinking. He can sense her feelings and know exactly what she is going to say or do next. User: What would be one theme of this story? Assistant: One theme of this story could be: being intuitive从样本中可以归纳出该数据集的几个关键特征:
- 对话固定由
User:与Assistant:前缀标注,角色轮换清晰,便于下游做角色切分; - 场景文本以两个空格缩进逐行呈现,Assistant 的续写内容同样保持缩进,保留原文的对白结构;
- 每条样本末尾通常会追加主题(theme)问答,为模型提供"从故事中归纳主题"的额外监督信号;
- 对话正文被存为单条字符串,不会拆分成结构化的多轮字段。
三、数据是怎么造出来的:模板系统与合成管线
数据集的合成逻辑全部实现在 prepare.py 中。该脚本以allenai/soda为源数据,逐条读取故事样本,通过一套固定模板 + 随机分支的策略,将"故事"重构为"对话指令任务"。
3.1 九大对话模板
脚本顶部集中定义了合成所用到的全部模板(模板中的{}为待填充字段):
| 模板常量 | 任务形态 | 填充字段 |
|---|---|---|
SUMMARY_TEMPLATE | 基于对话生成故事摘要 + 标题 | dialogue、story、title |
THEME_TEMPLATE | 抽取故事主题(追加段) | theme |
NEW_DIALOGUE_TEMPLATE | 基于故事新写一段对话 + 标题 | story、dialogue、title |
NEXT_LINES_TEMPLATE | 基于场景续写后续对白 + 标题 + 摘要 | scene、dialogue、title、story |
NEW_STORY_AND_DIALOGUE_TEMPLATE | 基于标题片段写故事 | title1、story |
FULL_DIALOGUE_TEMPLATE | 在故事后追加完整对话 | conversation、dialogue |
MORE_DIALOGUE_TEMPLATE | 在已有对话上追加更多对白 | conversation、dialogue1、title2、dialogue2 |
NEXT_DIALOGUE_TEMPLATE | "More please" 式追加对话 | conversation、dialogue1、dialogue2 |
NEW_STORY_AND_DIALOGUE_FROM_THEME_TEMPLATE | 基于主题写故事 + 对话 + 标题 | theme、story、dialogue、title |
以NEXT_LINES_TEMPLATE为例,其模板原文为:
User: Can you write the next few lines of dialogue for this scene: {scene} Assistant: Sure, the next dialogue for this scene could be: {dialogue} User: And a title? Assistant: Sure, a title for this dialogue could be: {title} User: How about a short description? Assistant: Sure, a short description for this dialogue could be: {story}对比第一节的示例样本可以发现,数据集卡片中的示例正是由该模板(并追加THEME_TEMPLATE)生成的,二者在措辞、缩进和轮次结构上完全一致。
3.2 主题(theme)的推导规则
SODA 原始数据中的每条样本带有relation(如xWant、xNeed等常识关系类型)和tail(关系宾语),prepare.py 据此推导出主题短语:
relation == "xWant"→theme = "wanting " + tail;relation == "xNeed"→theme = "needing " + tail;tail不以to、and开头 →theme = "being " + tail;tail以and开头 →theme = "people are " + tail.replace("and PersonY ", "");- 其余情况 → 直接取
tail。
随后还有两处全局清洗:theme.replace("PersonY", "another person")把模板中的泛指角色替换为更自然的表述,theme.replace("being is", "being")修正语法冗余。示例样本结尾的being intuitive正是该规则链的产物。
3.3 随机分支:从同一条故事派生五种任务
脚本使用random.seed(42)固定随机种子(保证可复现),对每条 SODA 样本按嵌套的随机数分支决定最终对话形态:
- 分支一(概率约 1/7):摘要任务——套用
SUMMARY_TEMPLATE,把完整对话作为输入、故事与标题作为输出;有 theme 则追加THEME_TEMPLATE; - 分支二(概率约 1/7):故事反写对话——套用
NEW_DIALOGUE_TEMPLATE,把故事作为输入、对话与标题作为输出; - 分支三(概率约 1/3):续写任务——把原始对话按尾部 3/4/5 行切分为"场景 + 续写"两段,套用
NEXT_LINES_TEMPLATE(卡片示例即来自此分支); - 分支四(概率约 1/3):故事 + 对话组合——以标题第一句为输入写故事,再以 50% 概率随机选择"完整对话""多续一段对话""More please 续写"等子分支,对应
FULL_DIALOGUE_TEMPLATE、MORE_DIALOGUE_TEMPLATE、NEXT_DIALOGUE_TEMPLATE; - 默认分支:主题驱动生成——套用
NEW_STORY_AND_DIALOGUE_FROM_THEME_TEMPLATE,以主题为输入生成故事、对话与标题。
这种"同一源数据、多样任务形态"的设计,天然制造了对话轮数、任务类型和长度分布的多样性,有助于提升下游模型的泛化能力。
3.4 输出格式
每条样本最终被序列化为一行 JSON,写入对应的data/{split}.jsonl:
output.write(f"{json.dumps({'conversation': conversation})}\n")即每条样本只有一个字段conversation(字符串),与数据集卡片中声明的 feature 完全对应。脚本同时支持--print参数(PRINT = len(sys.argv) > 1 and sys.argv[1] == "--print"),用于在控制台逐条预览生成的对话。
四、Hugging Face 加载脚本:把 jsonl 变成标准 dataset
为了将本地生成的 jsonl 文件包装成标准 Hugging Face 数据集,仓库提供了完整的加载脚本 soda_synthetic_dialogue.py,配套的公共配置与特征定义位于 hub.py。
关键实现点如下:
- 特征定义(hub.py):
features = datasets.Features({"conversation": datasets.Value("string")}),与卡片声明的单字段结构一致; - 配置定义:
BUILDER_CONFIGS中注册唯一配置soda_synthetic_dialogue_dialogue_modeling(schema="dialogue_modeling"),并设为DEFAULT_CONFIG_NAME,即数据集卡片中标注的config_name; - 数据源路径:
_URLS将 train/test/validation 三个切分映射到本地./data/train.jsonl、./data/test.jsonl、./data/validation.jsonl,正好对应 prepare.py 的产出目录结构; - 数据读取(_generate_examples):按行读取 jsonl,对每行
json.loads后以自增 key 逐条 yield,即标准GeneratorBasedBuilder的流式读法。
也就是说,完整的复现链路是:先运行prepare.py生成data/目录下的三个 jsonl 文件,再通过该加载脚本即可load_dataset("soda_synthetic_dialogue")得到带 train/test/validation 三个 split 的 Hugging Face 数据集对象。
五、在 Open-Assistant 训练链路中的实际接入方式
SODA Synthetic Dialogue 不仅是仓库中的一个数据集,还真实接入了模型训练端,可以从源码中看到两条接入路径。
5.1 训练端数据集类:SODADialogue
在 qa_datasets.py 中定义了SODADialogue类(model/model_training/custom_datasets/qa_datasets.py#L283-L315),它直接加载emozilla/soda_synthetic_dialogue:
- 遍历全部三个 split,对每条样本的
conversation字段按"User: "切分,丢弃首段空串; - 再对每一段按
"\nAssistant: "拆成「问题、回答」二元组; - 对切分失败(对话内容里混有多余的
User:/Assistant:标记)的样本计数为 fault 并跳过——这是对模板化数据中偶发格式噪声的容错处理; - 最终把每个对话整理成
(question, answer)交替的元组序列,供下游 SFT 使用。
可见该数据集在训练端是以纯对话建模方式使用的,与其dialogue_modeling的 schema 命名相呼应。
5.2 配置文件与数据集注册
- 在 custom_datasets/init.py 中,
soda_dialogue被注册为SODADialogue(data_path)(同文件soda则注册为原始 SODA 的SODA类); - 在 configs/config.yaml 的
sft_datasets列表中同时出现soda(带input_max_length: 1024参数)与soda_dialogue,说明两者可一起参与 SFT 训练; - 在 check_dataset_appearances.py 与 check_dataset_counts.py 的示例命令中,
soda均被列入 sft 模式的数据集名单,用于检查数据集出现次数与统计样本量。
此外,数据集卡片中的引用(citation)同样被完整固化在加载脚本的_CITATION字段中,确保使用该数据训练时可以被正确引用:
@article{ontocord2023sodasynth, author = {ontocord and Jeffrey Quesnelle}, title = {SODA Synthetic Dialogue}, year = {2023} }六、从零复现:本地生成与加载的完整步骤
综合上述源码,可以在本地完整复现该数据集的生成与加载流程:
- 准备源数据与依赖:安装
datasets与tqdm,确保可访问allenai/soda(prepare.py 通过load_dataset("allenai/soda")拉取原始数据); - 运行生成脚本:
python data/datasets/soda_synthetic_dialogue/prepare.py脚本会以random.seed(42)的固定随机序列,在data/目录下生成train.jsonl、test.jsonl、validation.jsonl三个 UTF-8 编码的 jsonl 文件,每行形如{"conversation": "User: ...\nAssistant: ..."}。如需预览生成效果可追加--print参数:
python data/datasets/soda_synthetic_dialogue/prepare.py --print加载为 Hugging Face 数据集:在
data/datasets/soda_synthetic_dialogue/目录下(或把_URLS指向对应 jsonl 路径后)使用datasets.load_dataset加载soda_synthetic_dialogue配置soda_synthetic_dialogue_dialogue_modeling,即可得到含 train/test/validation 三个 split、每个样本仅含conversation字段的标准数据集对象;接入训练:在模型训练端,可直接在 configs/config.yaml 的
sft_datasets中保留soda_dialogue(该名字对应SODADialogue类),训练脚本会自动完成对话切分与 QA 对抽取。
七、注意事项与使用边界
- 生成脚本中的缺陷需留意:在第四个随机分支中,代码无条件执行
title2 = title.split(".")[1],若title不含.会触发IndexError,这是脚本本身的容错盲区;同时第 87 行dat = dataset["train"][i]在所有 split 的循环中都固定取 train split 的数据,意味着 test/validation 切分实际上来源于 train 子集内的抽样顺序差异,而非源数据集的原始切分语义; - 数据为机器合成:卡片明确标注
annotations_creators: no-annotation、language_creators: machine-generated,语料由模板拼接而成,可能存在重复句式、格式噪声(训练端SODADialogue类专门统计的 fault 即为佐证),使用时建议配合数据清洗与去重; - 许可与合规:数据集采用 MIT 许可,符合 data/datasets/README.md 中"数据集必须具有宽松许可、不得包含儿童性虐待内容与个人隐私信息"的贡献要求;
- 适用任务:该数据集的 schema 为
dialogue_modeling,适合用于对话生成 / 指令跟随类模型的 SFT 训练,而非评测集或人工偏好数据。
八、小结
SODA Synthetic Dialogue 展示了 Open-Assistant 在数据工程上的一个典型范式:以开源故事语料为原料,用一套可复现的模板合成管线,低成本制造出海量、多样、可直接用于对话模型训练的指令数据。通过本仓库的生成脚本、加载脚本与训练端接入代码,你既可以一键复现该数据集,也能参考其模板设计思路,为自己的对话模型构造同类合成语料。
【免费下载链接】Open-AssistantOpenAssistant is a chat-based assistant that understands tasks, can interact with third-party systems, and retrieve information dynamically to do so.项目地址: https://gitcode.com/gh_mirrors/op/Open-Assistant
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考