File size: 5,403 Bytes
0dd9a1c | 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 | """
数据清洗脚本:从 07_oracle_results_nothink + 05_delta_frame_mapping 生成 RL 训练数据
过滤策略:
1. 非多选题(answer 不符合 A/A.xxx 格式)→ 丢弃
2. oracle_results 为空(推理失败)→ 丢弃
3. 单 pattern 条目(1个delta)→ 直接保留
4. 多 pattern 条目(2+个delta)→ 过滤掉含异常 oracle_answer 的 pattern
异常定义:oracle_answer 不是单个字母 A-E
"""
import json
from pathlib import Path
DATA_DIR = Path(__file__).resolve().parent
ORACLE_FILE = DATA_DIR / "07_oracle_results_nothink.json"
DELTA_FILE = DATA_DIR / "05_delta_frame_mapping.json"
OUTPUT_FILE = DATA_DIR / "08_rl_training_data.json"
VALID_ANSWERS = set("ABCDE")
def is_multiple_choice(answer: str) -> bool:
"""判断是否为标准多选题格式:A / A. xxx / A xxx"""
ans = answer.strip()
return (len(ans) == 1 and ans in VALID_ANSWERS) or \
(len(ans) >= 2 and ans[0] in VALID_ANSWERS and ans[1] in ". ")
def is_valid_oracle_answer(answer: str) -> bool:
"""oracle_answer 是否为合法的单字母选项"""
return answer.strip() in VALID_ANSWERS
def main():
# 加载 delta frame mapping
with open(DELTA_FILE) as f:
delta_data = json.load(f)
delta_map = {m["problem_id"]: m for m in delta_data["mappings"]}
print(f"[Delta] 加载 {len(delta_map)} 条")
# 加载 oracle results,同时构建 oracle 查找表
with open(ORACLE_FILE) as f:
oracle_data = json.load(f)
oracle_results = oracle_data["results"]
print(f"[Oracle] 加载 {len(oracle_results)} 条")
# 构建查找表:(problem_id, mask_delta_id) -> oracle entry
oracle_map = {}
for r in oracle_results:
pid = r["problem_id"]
for o in r.get("oracle_results", []):
oracle_map[(pid, o["mask_delta_id"])] = o
stats = {
"total_problems": len(oracle_results),
"skipped_not_mc": 0,
"skipped_empty_oracle": 0,
"skipped_no_delta": 0,
"output_problems": 0,
"single_pattern_kept": 0,
"multi_pattern_kept": 0,
"multi_pattern_abnormal_removed": 0,
"total_mask_patterns": 0,
"oracle_correct_count": 0,
}
training_data = []
for problem in oracle_results:
pid = problem["problem_id"]
# 过滤1:非多选题
if not is_multiple_choice(problem["gt_answer"]):
stats["skipped_not_mc"] += 1
continue
# 过滤2:oracle_results 为空
if not problem["oracle_results"]:
stats["skipped_empty_oracle"] += 1
continue
# 过滤3:没有 delta mapping
delta_item = delta_map.get(pid)
if delta_item is None:
stats["skipped_no_delta"] += 1
continue
delta_mask_patterns = delta_item.get("mask_patterns", [])
n_patterns = len(delta_mask_patterns)
# 构建 mask_patterns
mask_patterns = []
for p in delta_mask_patterns:
mid = p["mask_delta_id"]
oracle_entry = oracle_map.get((pid, mid))
oracle_correct = oracle_entry["oracle_correct"] if oracle_entry else None
# 多 pattern 时,过滤掉异常 oracle_answer 的 pattern
if n_patterns > 1 and oracle_entry is not None:
if not is_valid_oracle_answer(oracle_entry["oracle_answer"]):
stats["multi_pattern_abnormal_removed"] += 1
continue
pattern = {
"mask_delta_id": mid,
"zero_frame_indices": p["zero_frame_indices"],
"remaining_frames": p["remaining_frames"],
"mask_ratio": p["mask_ratio"],
"oracle_correct": oracle_correct,
}
mask_patterns.append(pattern)
stats["total_mask_patterns"] += 1
if oracle_correct:
stats["oracle_correct_count"] += 1
# 过滤掉清洗后 mask_patterns 为空的条目
if not mask_patterns:
continue
entry = {
"problem_id": pid,
"video": problem["video_path"],
"question": problem["question"],
"answer": problem["gt_answer"],
"n_sampled_frames": problem["n_sampled_frames"],
"mask_patterns": mask_patterns,
}
training_data.append(entry)
stats["output_problems"] += 1
if n_patterns == 1:
stats["single_pattern_kept"] += 1
else:
stats["multi_pattern_kept"] += 1
# 写出
n_total_p = stats["total_mask_patterns"]
output = {
"metadata": {
"total_entries": len(training_data),
"total_mask_patterns": n_total_p,
"oracle_correct_count": stats["oracle_correct_count"],
"oracle_correct_rate": round(stats["oracle_correct_count"] / n_total_p, 3) if n_total_p else 0,
"oracle_source": ORACLE_FILE.name,
"delta_source": DELTA_FILE.name,
},
"data": training_data,
}
with open(OUTPUT_FILE, "w", encoding="utf-8") as f:
json.dump(output, f, ensure_ascii=False, indent=2)
print(f"\n{'='*50}")
print(f"清洗完成,输出到 {OUTPUT_FILE.name}")
print(f"{'='*50}")
for k, v in stats.items():
print(f" {k}: {v}")
if __name__ == "__main__":
main()
|