doc_tree_val
收藏资源简介:
## 1. 数据组成 两文件均为**长度 100 的 JSON 数组**,按数据集分段连续排列,`uid` 从 `"0001"` 到 `"0100"`: | uid 区间 | dataset | 条数 | 分层方式 | |---|---|---:|---| | `0001`–`0015` | `qasper` | 15 | 文件前 15 条 | | `0016`–`0030` | `wikimqa` | 15 | 文件前 15 条 | | `0031`–`0045` | `hotpotqa` | 15 | 文件前 15 条 | | `0046`–`0060` | `musique` | 15 | 文件前 15 条 | | `0061`–`0085` | `lara` | **25** | **book 9 / financial 8 / paper 8** | | `0086`–`0100` | `longmemeval` | 15 | **5 种 question_type × 各 3 条** | **关于 LaRA 的 3 种文本类型**:25 无法被 3 整除,按 9/8/8 分配(余数给字典序最前的 `book`)。文本类型可直接从 `source_id` 解析,格式为 `lara32k-<book|financial|paper>-<文档名>`。 **关于 LongMemEval 的分层**:树文件按 question_type 分组存放,若直接取前 15 条会全部落在同一类型,因此改为每种类型取 3 条,覆盖全部 5 种类型。 --- ## 2. JSON 格式 ### 2.1 `sample_source_texts.json` 每条记录 5 个字段,所有 100 条 schema 完全一致: ```jsonc { "uid": "0061", // string, "0001"-"0100",两文件共用 "dataset": "lara", // string, 见上表 6 种取值 "source_id": "lara32k-book-Metamorphosis", // string, 该条在原数据集/树文件中的原始 ID "source_text_chars": 118410, // int, = len(source_text) "source_text": "One morning, when Gregor ..." // string, 完整原文 } ``` ### 2.2 `sample_tree_structures.json` 每条记录 6 个字段,所有 100 条 schema 完全一致: ```jsonc { "uid": "0061", // 与原文文件同 uid、同下标 "dataset": "lara", "source_id": "lara32k-book-Metamorphosis", "tree_stats": { ... }, // 见 2.3 "tree": { ... } // 见 2.4,原样保留的完整建树产物 } ``` ### 2.3 `tree_stats`(本次导出时统计,非原树自带) ```jsonc { "n_nodes": 39, // 递归计数的全部节点数 "n_root_nodes": 4, // 顶层节点数 "max_depth": 3, // 树深度(顶层为 1) "n_nodes_with_summary": 39, // summary 非空的节点数 "n_nodes_with_facts": 39, // facts 非空的节点数 "total_own_text_chars": 118410 // 各节点 own_text_chars 之和 } ``` ### 2.4 `tree`:原样保留的建树产物 ```jsonc { "doc_id": "...", "total_chunks": 7, "total_tokens": 5163, "structure": [ ... ], // 节点数组,见下 "structure_quality": { ... }, // 建树时的自检报告(passed / issues 等) "operator_report": { ... }, // 建树算子轨迹:anchor_report / trace / enrich_pass_trace "_build_seconds": 41.4 } ``` `structure` 是**嵌套**节点数组,子节点放在 `nodes` 字段里(叶节点没有该字段)。单个节点: | 字段 | 类型 | 含义 | |---|---|---| | `node_id` | string | 节点 ID,如 `"n1"` | | `structure` | string | 层级编号,如 `"1"`、`"1.2"` | | `title` | string | 标题 | | `heading_level` | int | 标题层级 | | `char_start` / `char_end` | int | 该节点**及其所有子节点**在原文中的整体区间 | | `start_index` / `end_index` | int | 对应的 chunk 序号区间 | | `own_chunk_ids` | list[string] | 本节点自身持有的 chunk ID | | `own_text_chars` | int | 本节点自身持有的字符数(= `len(source_text)`) | | `source_text` | string | **本节点自身持有的原文**(不含子节点的部分;纯容器节点为空串) | | `heading_anchor` | object | 标题锚点验证信息 | | `token_count` | int | token 数 | | `summary` | string | 摘要 | | `facts` | list[string] | 原子事实 | | `nodes` | list[object] | 子节点数组(叶节点无此字段) | > ⚠️ **重要(容易踩的坑)**:父节点的 `[char_start, char_end)` **包含**其所有子节点的区间,但父节点的 `source_text` **只存自己那部分**。 > > 因此**不能**把所有节点拉平后按 `char_start` 排序、拼接 `source_text` 来还原原文——这会让父子文本交错、产生虚假的不匹配。 > > 正确做法:每个节点自身存储的文本占据 `[char_start, char_start + len(source_text))`,把这些区间取并集即可。`export_sample_pairs.py` 的 `verify()` 正是按此实现。 --- ## 3. 各数据集规模概览 | dataset | 原文字符数 均值(范围) | 节点数 均值(范围) | 树深度 范围 | |---|---|---|---| | `qasper` | 21,652 (13,849–35,614) | 15.3 (6–25) | 1–3 | | `wikimqa` | 30,641 (7,540–62,298) | 43.8 (19–72) | 2–4 | | `hotpotqa` | 57,386 (7,939–80,499) | 75.0 (19–129) | 2–5 | | `musique` | 70,139 (55,673–76,824) | 88.4 (41–135) | 3–5 | | `lara` | 111,624 (69,258–157,815) | 39.0 (**1**–93) | 1–7 | | `longmemeval` | 494,557 (480,549–509,951) | 96.9 (44–440) | 1–4 | > 注:LaRA 有文档的树只有 **1 个节点**(整篇不切分),集中在 book 类语料 —— 这是该语料建树粒度偏粗的已知现象,使用这批样例做分析时需注意。 --- ## 4. 使用示例 ```python import json src = json.load(open("sample_source_texts.json")) trees = json.load(open("sample_tree_structures.json")) # 两文件同序、同 uid,可直接按下标配对 for s, t in zip(src, trees): assert s["uid"] == t["uid"] and s["source_id"] == t["source_id"] # 按 uid 建索引 by_uid = {s["uid"]: (s, t) for s, t in zip(src, trees)} s, t = by_uid["0061"] # 按数据集筛选 lara = [(s, t) for s, t in zip(src, trees) if s["dataset"] == "lara"] # LaRA 文本类型从 source_id 解析 book = [(s, t) for s, t in lara if s["source_id"].split("-")[1] == "book"] # 遍历树节点(注意子节点在 nodes 字段) def walk(nodes, depth=0): for n in nodes: yield depth, n yield from walk(n.get("nodes") or [], depth + 1) for depth, n in walk(t["tree"]["structure"]): print(" " * depth, n["node_id"], n["title"], f"({n['own_text_chars']}c)") ```



