flashtrace / exp /exp2 /DATASETS.md
wenbopan's picture
Sync FlashTrace package from GitHub
55b60a8
# exp/exp2 数据集与样本流说明
本文件说明 Experiment 2 中支持的数据集、样本结构,以及在「采样阶段」与「归因阶段」的处理方式。
## 支持的数据集
- `morehopqa``data/with_human_verification.json`
- RULER 系列 JSONL:`hotpotqa_long``niah_*``vt_*`(自动在 `data/ruler_multihop/<len>/.../validation.jsonl` 搜索),或直接传入任意 RULER JSONL 路径
- 其余数据集(如 math)被显式跳过
- 归因阶段同样优先使用缓存文件 `exp/exp2/data/<name>.jsonl`,否则按上述规则解析;传入存在的 JSONL 路径也会按 RULER 结构加载
### 共同的样本字段定义
```json
{
"prompt": "<上下文+问题>",
"target": "<答案或生成>",
"indices_to_explain": [start_tok, end_tok] | null, // token-level:需要解释的 generation token span(闭区间)
"attr_mask_indices": [...], // legacy:覆盖率金标句子索引(当前 exp2 不再使用),可能为 null
"sink_span": [start, end] | null, // 生成 token 中的答案片段
"thinking_span": [start, end] | null, // 生成 token 中的 CoT 片段
"metadata": { ... } // 数据集特定元信息
}
```
- **`CachedExample`**`dataset_utils.py` 统一的内存态结构,字段与上述 JSON 完全一致,用于采样阶段(加载原始数据)与归因阶段(加载缓存或原始)。
- **缓存行(JSONL)**`sample_and_filter.py` 写入的每行 JSON,与 `CachedExample` 字段一一对应。
- **采样阶段处理流(通用)**
1. 加载原始数据集样本(`prompt`/`indices_to_explain` 等保持一致)。
2. 按模板调用生成模型,要求「思考文本 + 末尾 \\box{} 答案」。
3. 若生成不符合「思考 + 单个 \\box{} 且无尾巴」的格式,直接丢弃该样本。
4. 提取思考片段与 `\\box{}` 内文本,仅用 `\\box{}` 内文调用判定模型。
5. 判定为 True 时,重新拼接「思考片段 + 去除 box 包裹的答案文本」作为 `target`,并据此记录 `sink_span`/`thinking_span`
6. 写入缓存:只保留 `reference_answer``judge_response`(可选 `boxed_answer`),不再存储 `candidate_answer`
### 生成切分与 span 解析
- `split_boxed_generation``dataset_utils.py`)校验格式:必须是「非空思考文本 + 单个末尾 \\box{}」且箱体之后无其他字符,否则直接跳过。
- `target` 由「思考片段 + 换行 + 最终答案文本(无 box)」重组。
- `attach_spans_from_answer` 使用 tokenizer 的 offset mapping 将最终答案在 `target` 中的字符区间映射到 token 级索引,得到 `sink_span``thinking_span` 取从开头到 `sink_span` 前一 token 的闭区间。两者均为 token 级 span,满足后续多跳 IFR 的调用约定。
- `indices_to_explain` 在采样写缓存时统一设置为 `sink_span`(boxed 内文在 `target` 中对应的 generation token span)。
---
## MoreHopQA
- **原始样本结构(`MoreHopQAAttributionDataset` → `CachedExample`)**
```json
{
"prompt": "<context 拼接>\\n<question>",
"target": null,
"indices_to_explain": null,
"attr_mask_indices": null,
"sink_span": null,
"thinking_span": null,
"metadata": {
"answer": "<gold answer>",
"_id": "<example id>",
"original_context": <原始上下文结构>
}
}
```
- 加载时机:`DatasetLoader.load_raw("morehopqa")` 在采样阶段、归因阶段(无缓存时)都会产出 `CachedExample`
- 说明:exp2 的 token-level row/rec 需要 `target` + 可定位的答案 token span;建议先跑 `sample_and_filter.py` 产出缓存后再做归因评估。
- **采样阶段(生成 & 过滤后写缓存)**
```json
{
"prompt": "<同上>",
"target": "<生成的 CoT + 最终答案文本(已去掉 box 包裹)>",
"indices_to_explain": [start_tok, end_tok],
"attr_mask_indices": null,
"sink_span": [start_tok, end_tok] | null,
"thinking_span": [start_tok, end_tok] | null,
"metadata": {
"answer": "<gold answer>",
"_id": "<example id>",
"original_context": <原始上下文结构>,
"reference_answer": "<gold answer>",
"judge_response": "<True/False 文本>",
"boxed_answer": "<可选,boxed 解析结果>"
}
}
```
- `sink_span`/`thinking_span`:仅在成功解析 `\\box{}` 时填充;`target` 为「思考 + 最终答案文本」的裁剪版。
- 写入:`exp/exp2/data/morehopqa.jsonl`
- **归因阶段(加载缓存优先)**
- 加载:`run_exp.py` 优先 `load_cached`(JSONL → `CachedExample`),否则回退原始结构并在线生成 `target`
- 使用:忠实度(token-level RISE/MAS)直接用缓存的 `target``ifr_multi_hop` 在有 `sink_span`/`thinking_span` 时限定答案/CoT,否则视整个生成为 sink。
---
## RULER 热点问答(`hotpotqa_long`)
- **原始样本结构(`RulerAttributionDataset` → `CachedExample`)**
```json
{
"prompt": "<input> + <answer_prefix>",
"target": "<answer_prefix + sep + ', '.join(outputs)>",
"indices_to_explain": [0],
"attr_mask_indices": [<句子索引>...] | null,
"sink_span": null,
"thinking_span": null,
"metadata": {
"dataset": "ruler",
"length": <int>,
"length_w_model_temp": <any>,
"outputs": [...],
"answer_prefix": "<str>",
"token_position_answer": <any>,
"needle_spans": [
{
"title": "<str>",
"doc_index": <int>,
"document_number": <int>,
"sentence_index": <int>,
"sentence": "<str>",
"context_span": [start, end],
"span": [start, end],
"snippet": "<str>"
},
...
],
"prompt_sentence_count": <int>,
"reference_answer": "<在 loader 中补充,来自 outputs 或 target>"
}
}
```
- 加载时机:`DatasetLoader.load_raw("hotpotqa_long")` 在采样阶段、归因阶段(无缓存时)都会产出 `CachedExample`。
- **采样阶段(生成 & 过滤后写缓存)**
```json
{
"prompt": "<同上>",
"target": "<生成的 CoT + 最终答案文本(已去掉 box 包裹)>",
"indices_to_explain": [-2],
"attr_mask_indices": [<句子索引>...] | null,
"sink_span": [start_tok, end_tok] | null,
"thinking_span": [start_tok, end_tok] | null,
"metadata": {
"dataset": "ruler",
"length": <int>,
"length_w_model_temp": <any>,
"outputs": [...],
"answer_prefix": "<str>",
"token_position_answer": <any>,
"needle_spans": [...],
"prompt_sentence_count": <int>,
"reference_answer": "<outputs 拼接或 target>",
"judge_response": "<True/False 文本>",
"boxed_answer": "<可选>"
}
}
```
- `attr_mask_indices` 保留原值;`indices_to_explain` 统一为末句 `[-2]`(最后一个非 EOS 生成句);`sink_span`/`thinking_span` 仅在成功解析 `\\box{}` 时填充;`target` 为「思考 + 最终答案文本」的裁剪版。
- 写入:`exp/exp2/data/hotpotqa_long.jsonl`
- **归因阶段(加载缓存优先)**
- 加载:优先 `load_cached`(JSONL → `CachedExample`),否则回退原始解析。
- 使用:覆盖率使用 `attr_mask_indices`;忠实度与 `ifr_multi_hop` 利用缓存的 `sink_span`/`thinking_span` 定位答案/CoT,若缺失则视整个生成为 sink。
---
## RULER NIAH / Variable Tracking(`niah_*`, `vt_*`)
- **原始样本结构(同 RULER 通用)**
```json
{
"prompt": "<input> + <answer_prefix>",
"target": "<answer_prefix + sep + ', '.join(outputs)>",
"indices_to_explain": [0],
"attr_mask_indices": [<句子索引>...] | null,
"sink_span": null,
"thinking_span": null,
"metadata": {
"dataset": "ruler",
"length": <int>,
"length_w_model_temp": <any>,
"outputs": [...],
"answer_prefix": "<str>",
"token_position_answer": <any>,
"needle_spans": [...],
"prompt_sentence_count": <int>,
"reference_answer": "<在 loader 中补充>"
}
}
```
- 加载时机:`DatasetLoader.load_raw("<niah_* 或 vt_*>")` 在采样阶段、归因阶段(无缓存时)使用。
- **采样阶段(生成 & 过滤后写缓存)**
```json
{
"prompt": "<同上>",
"target": "<思考 + 最终答案文本(无 box),无其他尾巴>",
"indices_to_explain": [start_tok, end_tok],
"attr_mask_indices": [<句子索引>...] | null,
"sink_span": [start_tok, end_tok] | null,
"thinking_span": [start_tok, end_tok] | null,
"metadata": {
"dataset": "ruler",
"length": <int>,
"length_w_model_temp": <any>,
"outputs": [...],
"answer_prefix": "<str>",
"token_position_answer": <any>,
"needle_spans": [...],
"prompt_sentence_count": <int>,
"reference_answer": "<outputs 拼接或 target>",
"judge_response": "<True/False 文本>",
"boxed_answer": "<可选>"
}
}
```
- 生成/判定流程与 `hotpotqa_long` 相同;`target` 是裁剪后的「思考 + 最终答案文本」。
- 写入:`exp/exp2/data/<dataset>.jsonl`(例如 `niah_mq_q2.jsonl`, `vt_h6_c1.jsonl`)。
- **归因阶段(加载缓存优先)**
- 与 `hotpotqa_long` 相同:优先缓存,否则原始;恢复率(`recovery_ruler`)使用 `metadata.needle_spans`(映射到 prompt tokens);多跳 IFR 在有 `sink_span`/`thinking_span` 时作用于答案/CoT。
---
## `indices_to_explain` 约定
- token-level:`indices_to_explain = [start_tok, end_tok]`(闭区间),坐标系为 `tokenizer(target, add_special_tokens=False)` 的 generation token indices。
- exp2 推荐:`indices_to_explain == sink_span`,即 boxed 内文(最终答案)在 `target` 中对应的 token span。
---
## 自定义 RULER JSONL 路径
- 若 `--dataset` 传入存在的 JSONL 路径,`dataset_from_name` 按 RULER 文件解析,字段与流程同 RULER 系列。
- 采样、归因阶段行为与上文 RULER 描述一致,只是文件名由显式路径决定。
---
## 归因阶段加载优先级与效果
- `run_exp.py` 加载顺序:`exp/exp2/data/<name>.jsonl` 缓存 > 显式给定的 JSONL 路径 > 原始解析(MoreHopQA 或 RULER)
- 恢复率 (`mode=recovery_ruler`) 仅支持 RULER(要求 `metadata.needle_spans`),否则拒绝
- 忠实度 (`mode=faithfulness_gen`) 使用生成文本;`ifr_multi_hop` 在有 `sink_span`/`thinking_span` 时才对答案/CoT 做多跳,否则退化为整段生成