File size: 10,581 Bytes
55b60a8 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 | # 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 做多跳,否则退化为整段生成
|