Open-Assistant StackExchange 指令数据集构建全流程解析:从 XML 归档到 Parquet 训练数据
2026/9/19 11:51:34 网站建设 项目流程

Open-Assistant StackExchange 指令数据集构建全流程解析:从 XML 归档到 Parquet 训练数据

【免费下载链接】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

本文以 Open-Assistant 仓库中 data/datasets/oa_stackexchange 目录为线索,完整讲解如何将 Stack Exchange 全站历史问答归档(Internet Archive 提供的 XML 转储)转换为可用于 SFT 指令微调的 Instruction 数据集。读者将掌握「下载 → XML 流式解析 → 过滤与格式转换 → 统计 → 合并 → 上传 Hugging Face」六阶段流水线的每步操作、关键参数(长度阈值、评分阈值、每站最大答案数)及对应的源码实现细节,并能直接复现出与仓库发布版本一致的数据集。

一、数据集概览:What & Why

Stack Exchange 是互联网上规模最大的高质量问答社区网络,覆盖编程、数学、物理、烹饪、棋类等数百个垂直主题站点。Open-Assistant 从 Internet Archive 的 stackexchange 归档(下称"归档")中提取帖子数据,构建了名为Open-Assistant StackExchange Instruction(pretty name)的指令数据集。

该数据集最终以单个合并后的 parquet 文件形式发布,并带有以下数据集卡片信息(见 data/datasets/oa_stackexchange/README.md 的 YAML frontmatter):

字段
数据列INSTRUCTION、RESPONSE、SOURCE、METADATA(answer_score、question_score、tags)
splitstrain 单折,6,331,083 条样本
数据大小dataset_size ≈ 6,549,838,664 字节(约 6.1 GiB),download_size ≈ 3,755,782,987 字节
licensecc-by-sa-4.0(遵循 Stack Exchange 数据许可)
languageen、uk、ru、de、fr、it、es 等

数据源于归档中各站点公开的Posts.xml文件,原始提取代码由 b-mc2 定义的 Instruction 数据集规范(四列:INSTRUCTIONRESPONSESOURCEMETADATA)。

数据集的核心过滤规则

归档中包含各站点的全部帖子(问题、回答、评论等),本数据集按如下规则过滤(README.md 与 process.py 双重印证):

  • 仅保留有被采纳回答(accepted answer)的帖子线程
  • 问题与回答的字符长度均小于 1000 字符(源码常量MAX_LENGTH = 1000);
  • 其他回答、无采纳答案的问题、过长条目一律丢弃。

也就是说,每一行代表一个「问题 → 被采纳回答」的高质量问答对,天然契合指令微调所需的 INSTRUCTION/RESPONSE 结构。

每行数据结构

每行包含四列:

  • INSTRUCTION:由问题标题(Title)与问题正文(Body,HTML 转 Markdown 后)拼接而成,格式为Title\nBody_markdown
  • RESPONSE:被采纳回答正文(HTML 转 Markdown 后);
  • SOURCE:固定格式stackexchange-{site_name},例如stackexchange-ai,用于标注数据出处,便于训练时做去重与溯源;
  • METADATA:JSON 结构,包含tags(站点标签,如python pandas)、question_score(问题评分)、answer_score(回答评分)。

二、数据集复现流水线(五步操作)

仓库在oa_stackexchange目录下提供了完整的可复现脚本链,依次执行即可从原始 XML 归档重建整个数据集:

第 1 步:下载全部 XML 文件

将归档中所有站点的Posts.xml下载到xml/目录:

./download.py

download.py 的实现要点:

  • 先请求归档目录页,解析 HTML 表格拿到全部*.7z归档文件名,并对站点名做归一化处理(如meta.stackoverflow.com转为meta_stackoverflow),再拼出view_archive.php的直链;
  • 使用ThreadPoolExecutorNUM_PARALLEL = 20)并发下载,跳过_metastackoverflow.com-命名的文件(RE_IGNORE正则);
  • 已有缓存(xml/{name}.xml存在)时直接复用,避免重复下载;
  • 注意stackoverflow.com-Posts.7z体积约 18GB,脚本特意提示手动下载或用 torrent(stackexchange_archive.torrent的 658 号文件),解压后重命名为xml/stackoverflow.xml放入目录即可。

第 2 步:解析 XML、过滤并转换为 OA 格式

./process.py

不带参数时,process.py 会自动扫描xml/*.xml逐个处理;也可以显式传入站点名只处理特定站点。每个站点输出到parquet/{site}.parquet

第 3 步:统计各站点样本数

./stats.py

stats.py 遍历parquet/下所有 parquet 文件,用 pyarrow 的ParquetDataset统计每个站点的行数并打印- {topic}: {count:,}格式的清单(即 README 中统计列表的来源)。

第 4 步:合并为单一 parquet 文件

./combine.py

combine.py 将full/目录下的 parquet 全部合并为stackexchange.parquet,合并前会再做一次长度收紧(INSTRUCTION 与 RESPONSE 均 < 1000 字符)并清洗 METADATA 字段,打印每个文件的缩短比例;合并逻辑由 merge_parquets.py 实现——它通过 pyarrow 流式读写(ParquetWriter逐表写入、iter_batches逐批读取),并用coalesce按行数(默认max_size = 2**20)分批拼接,避免大文件一次性载入内存。

第 5 步:上传 Hugging Face Hub

./upload.py

先执行huggingface-cli login完成认证,然后运行:

./upload.py

upload.py 使用datasets库加载本地stackexchange.parquetpush_to_hubmax_shard_size="500MB"自动分片),最终上传到donfu/oa-stackexchange

完整流程也符合仓库总则 data/datasets/README.md 中「Convert → Login → Push → 更新 HF README」的规范步骤。

三、源码级深入:process.py 的解析与过滤原理

3.1 流式 SAX 解析,应对超大 XML

Stack Exchange 的 Posts.xml 动辄数 GB(如 stackoverflow 的 Posts.xml 超过 60GB),无法整体载入内存。parse_and_convert采用lxml.etree.iterparseend 事件流式解析

context = etree.iterparse(path, events=("end",)) for _, element in tqdm(context, total=total_rows): if element.tag == "row": if len(element.get("Body")) > MAX_LENGTH: continue rows.append(parse_row(element)) element.clear() while element.getprevious() is not None: del element.getparent()[0]

关键点:

  • 预先用grep -c '<row'统计总行数用于 tqdm 进度条;
  • MAX_LENGTH = 1000:正文超长直接跳过(这是 README 所述「长度 < 1000」的底层实现);
  • element.clear()配合删除前序兄弟节点,及时释放已处理节点的内存;
  • 每处理10**6行或全部完成时,将累积行构造 DataFrame、调用convert_to_oa转换,随后del df / del oa / gc.collect()强制回收内存——即分块批处理模式。

parse_row从 XML 属性中提取Id、PostTypeId、Body、Title、Tags、Score、AcceptedAnswerId、ParentId八个字段(缺失字段用 0 或空串兜底)。

3.2 问答配对:以内连接实现「有采纳答案」过滤

convert_to_oa是过滤逻辑的核心:

questions = all[all["AcceptedAnswerId"] != 0] merged = pd.merge( questions, all, how="inner", left_on="AcceptedAnswerId", right_on="Id", suffixes=("_q", "_a"), )

即:先筛出所有带AcceptedAnswerId的问题,再与该问题表自身按AcceptedAnswerId == Id做 inner join,把问题行与被采纳回答行拼成一对。没有采纳答案的问题、以及未被采纳的其他回答都在这一步自然被丢弃。

之后构造四列:

merged["INSTRUCTION"] = merged["Title_q"] + "\n" + merged["Body_q"].apply(to_markdown) merged["RESPONSE"] = merged["Body_a"].apply(to_markdown) merged["SOURCE"] = source merged["METADATA"] = merged.apply(create_metadata, axis=1)

注意脚本还定义了MAX_ANSWERS = 10QUESTION_SCORE_TRESHOLD = 0ANSWER_SCORE_TRESHOLD = 0等常量,其中后两者在 join 前可作为评分下限过滤的扩展点(当前版本未启用该过滤,仅保留常量)。

3.3 正文清洗:HTML → Markdown,去链接去 emoji

问答正文是 Stack Exchange 的 HTML 片段,直接喂给模型会引入大量噪声。to_markdown做了三级清洗:

  1. html2text(text, bodywidth=0)将 HTML 转为 Markdown,转换失败时退化为正则剔除所有<...>标签;
  2. 用正则\[([^\]]+)\]\(([^\)]+)\)将 Markdown 链接text替换为纯文本text,再用https?:\/\/[^\s]+删除残余裸链接;
  3. remove_emojis按 Unicode 区块(emoticons、symbols & pictographs、transport & map、flags、dingbats 等)剔除 emoji。

3.4 标签清洗与元数据

convert_tags将 Stack Exchange 的><分隔的原始标签串(如<python><pandas>)转换为人类可读的逗号分隔文本(python, pandas),同时把-替换为空格(如machine-learningmachine learning)。create_metadata最终产出:

{ "tags": "python, pandas", "question_score": 42, "answer_score": 7, }

3.5 落盘规范

save_parquet使用 pandas 的to_parquet并强制row_group_size=100, engine="pyarrow", index=False——这正是 data/datasets/README.md 规定的仓库级数据格式要求(parquet 必须带row_group_size=100index=False),保证全仓库数据集格式统一。

四、统计结果:数据集覆盖面与规模

README 中列出的按站点统计的样本数(来源于stats.py输出)展示了该数据集的广度。以下节选若干代表站点:

站点样本数站点样本数
stackoverflow4,269,779ru_stackoverflow106,714
math501,019serverfault81,229
superuser128,488askubuntu78,472
unix54,338gaming44,899
english42,415electronics41,717
tex42,808physics35,386
apple32,603gis30,249
ell30,428salesforce27,962
pt_stackoverflow27,650blender25,527
wordpress24,270sharepoint24,934
......合计约 180 个站点6,331,083

值得注意的几点:

  • 长尾明显:stackoverflow 与 math 两个站点贡献了约 75% 的样本;而bioacoustics(70)、cseducators(71)、conlang(101)、interpersonal(199)等小众站点样本极少,为数据集带来跨领域多样性;
  • 多语言覆盖:包含es_stackoverflowja_stackoverflowpt_stackoverflowru_stackoverflowukrainianchinesejapanesefrenchgerman等非英语站点,与数据集卡片标注的 7 种语言一致,可用于多语言 SFT 训练;
  • 含 meta 站点meta_stackoverflowmeta_askubuntu等元站点也被收录,需注意这类站点内容以社区治理讨论为主,使用时可结合 METADATA 的 tags 按需过滤。

五、数据集在训练链路中的使用方式

该数据集按仓库规范属于Instruction 数据集(data/datasets/README.md 中明确定义:必须含INSTRUCTIONRESPONSESOURCEMETADATA四列)。在模型训练侧,model/model_training的 SFT 训练器会读取这类指令样本并与对话式数据混合使用——以 model/model_training/custom_datasets/oasst_dataset.py 为例,其load_oasst_export会按DatasetEntrySftconversationUtterance(text, role, ...)序列组成)组织样本,roleprompter/assistant,对应到本数据集即为INSTRUCTION → prompterRESPONSE → assistant。用户可仿照该模式,将本数据集的问答对映射为 SFT 对话样本参与微调;METADATA 中的question_scoreanswer_score还可作为质量加权或过滤的依据。

六、复现注意事项与边界

  • 磁盘与内存:全部 XML 解压后体积庞大(仅 stackoverflow 一项约 60GB+),建议按站点分批处理,且process.py的分块批处理与merge_parquets.py的流式合并都明确针对大文件场景设计;
  • 依赖process.py需要pandaslxmlhtml2texttqdmpyarrowdownload.py需要requestsbeautifulsoup4upload.py需要datasetshuggingface_hub
  • 网络要求:归档托管于 Internet Archive,下载依赖其可用性;stackoverflow.com-Posts.7z建议走官方提示的 torrent 方式;
  • 许可证:数据遵循 Stack Exchange 的 CC BY-SA 4.0 许可(归档详情页亦有说明),使用与再分发时须保留署名并遵守相同许可条款;
  • 上游引用:原始提取与转换思路来自 b-mc2 的公开代码,本仓库脚本在此基础上适配了 Open-Assistant 的四列 Instruction 格式。

七、小结

oa_stackexchange数据集是 Open-Assistant 训练数据体系中最具规模的指令数据集之一:它以 Stack Exchange 的「问题 + 被采纳回答」结构为天然指令对,通过「仅保留有采纳答案、双方均 <1000 字符」的过滤策略控制质量与长度,借助iterparse流式解析 + pandas 批处理 + pyarrow 流式合并解决了超大数据集的内存瓶颈,最终以统一四列 Schema 的 parquet 文件交付。仓库内的 download.py、process.py、stats.py、combine.py、upload.py 五步脚本链即为完整可复现的参考答案,开发者可在此基础上调整长度阈值、评分阈值或站点白名单,构建属于自己的 Stack Exchange 指令数据集。

【免费下载链接】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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询