Commit 03dd9219 03dd92196d55822e6cd885f1ffe25b7b0c2642ac by 沈秋雨

fix

1 parent 04c78716
...@@ -11,7 +11,7 @@ python results/fix_same_singer_samples.py \ ...@@ -11,7 +11,7 @@ python results/fix_same_singer_samples.py \
11 --input results/dna_eval.csv \ 11 --input results/dna_eval.csv \
12 --queries downloads_db/queries.csv \ 12 --queries downloads_db/queries.csv \
13 --output results/dna_eval_fixed.csv \ 13 --output results/dna_eval_fixed.csv \
14 --threshold 0.05 14 --threshold 0.01
15 """ 15 """
16 16
17 import argparse 17 import argparse
...@@ -70,20 +70,21 @@ def compute_summary(rows: list[dict], threshold: float, out_path: str) -> dict: ...@@ -70,20 +70,21 @@ def compute_summary(rows: list[dict], threshold: float, out_path: str) -> dict:
70 """计算评测指标。""" 70 """计算评测指标。"""
71 tp = fp = tn = fn = 0 71 tp = fp = tn = fn = 0
72 upload_ms, poll_ms, total_ms = [], [], [] 72 upload_ms, poll_ms, total_ms = [], [], []
73 by_variant: dict[str, dict] = {} 73 # 按 (sample_class, variant) 逐行统计,避免同一 variant 跨类混淆
74 by_class_variant: dict[str, dict[str, dict]] = {}
74 75
75 for r in rows: 76 for r in rows:
76 pred = r.get("predicted_duplicate", "").lower() == "true" 77 pred = r.get("predicted_duplicate", "").lower() == "true"
77 exp = r.get("expected_duplicate", "").lower() == "true" 78 exp = r.get("expected_duplicate", "").lower() == "true"
78 correct = r.get("correct", "").lower() == "true" 79 correct = r.get("correct", "").lower() == "true"
79 sample_class = r.get("sample_class", "unknown") 80 sample_class = r.get("sample_class", "unknown")
80
81 variant = r.get("variant", "unknown") 81 variant = r.get("variant", "unknown")
82 if variant not in by_variant: 82
83 by_variant[variant] = {"correct": 0, "total": 0, "sample_class": sample_class} 83 cls_dict = by_class_variant.setdefault(sample_class, {})
84 by_variant[variant]["total"] += 1 84 entry = cls_dict.setdefault(variant, {"correct": 0, "total": 0})
85 entry["total"] += 1
85 if correct: 86 if correct:
86 by_variant[variant]["correct"] += 1 87 entry["correct"] += 1
87 88
88 if exp and pred: 89 if exp and pred:
89 tp += 1 90 tp += 1
...@@ -127,6 +128,12 @@ def compute_summary(rows: list[dict], threshold: float, out_path: str) -> dict: ...@@ -127,6 +128,12 @@ def compute_summary(rows: list[dict], threshold: float, out_path: str) -> dict:
127 "max": round(s[-1], 1), 128 "max": round(s[-1], 1),
128 } 129 }
129 130
131 def _variant_stats(cls_dict):
132 return {
133 v: {"accuracy": round(d["correct"] / d["total"], 4), "total": d["total"]}
134 for v, d in sorted(cls_dict.items())
135 }
136
130 return { 137 return {
131 "total": total, 138 "total": total,
132 "duplicate_threshold": threshold, 139 "duplicate_threshold": threshold,
...@@ -139,23 +146,9 @@ def compute_summary(rows: list[dict], threshold: float, out_path: str) -> dict: ...@@ -139,23 +146,9 @@ def compute_summary(rows: list[dict], threshold: float, out_path: str) -> dict:
139 "upload_time_ms": _stats(upload_ms), 146 "upload_time_ms": _stats(upload_ms),
140 "poll_time_ms": _stats(poll_ms), 147 "poll_time_ms": _stats(poll_ms),
141 "by_sample_class": { 148 "by_sample_class": {
142 cls: { 149 cls: {"by_variant": _variant_stats(by_class_variant.get(cls, {}))}
143 "by_variant": {
144 v: {"accuracy": round(d["correct"] / d["total"], 4), "total": d["total"]}
145 for v, d in sorted(by_variant.items())
146 if d["sample_class"] == cls
147 }
148 }
149 for cls in ("positive", "negative") 150 for cls in ("positive", "negative")
150 }, 151 },
151 "by_variant": {
152 v: {
153 "sample_class": d["sample_class"],
154 "accuracy": round(d["correct"] / d["total"], 4),
155 "total": d["total"],
156 }
157 for v, d in sorted(by_variant.items())
158 },
159 "out": out_path, 152 "out": out_path,
160 } 153 }
161 154
...@@ -196,7 +189,7 @@ def main(): ...@@ -196,7 +189,7 @@ def main():
196 189
197 print(f"原始行数: {len(rows)}") 190 print(f"原始行数: {len(rows)}")
198 191
199 removed = [] 192 relabeled = []
200 kept = [] 193 kept = []
201 for r in rows: 194 for r in rows:
202 if r.get("sample_class") != "negative": 195 if r.get("sample_class") != "negative":
...@@ -214,23 +207,30 @@ def main(): ...@@ -214,23 +207,30 @@ def main():
214 main_dur = main_durations.get(song_id) 207 main_dur = main_durations.get(song_id)
215 cover_dur = cover_durations.get(r.get("audio_path", "")) 208 cover_dur = cover_durations.get(r.get("audio_path", ""))
216 209
210 is_same_recording = False
217 if main_dur and cover_dur: 211 if main_dur and cover_dur:
218 diff_ratio = abs(cover_dur - main_dur) / main_dur 212 is_same_recording = abs(cover_dur - main_dur) / main_dur < args.threshold
219 if diff_ratio < args.threshold:
220 removed.append(r)
221 continue
222 elif main_dur: 213 elif main_dur:
223 # 没有 cover 时长信息但同歌手,保守移除 214 is_same_recording = True # 没有 cover 时长,保守认为同录音
224 removed.append(r) 215
225 continue 216 if is_same_recording:
217 # 重新标注为正样本:同录音跨平台分发,应该匹配
218 r = dict(r)
219 r["expected"] = "duplicate"
220 r["sample_class"] = "positive"
221 r["expected_song_id"] = song_id
222 # 重算 correct:预测为 duplicate 才算对
223 predicted = r.get("predicted_duplicate", "").lower() == "true"
224 r["correct"] = str(predicted)
225 relabeled.append(r)
226 226
227 kept.append(r) 227 kept.append(r)
228 228
229 print(f"移除行数: {len(removed)}(同歌手且时长相近)") 229 print(f"重新标注行数: {len(relabeled)}(同歌手且时长相近,改为 expected=duplicate)")
230 print(f"保留行数: {len(kept)}") 230 print(f"保留行数: {len(kept)}")
231 231
232 # 写修复后的文件(去掉冗余列) 232 # 写修复后的文件(去掉冗余列)
233 DROP_COLS = {"topk_hit", "expected_duplicate", "predicted_duplicate", "sample_class"} 233 DROP_COLS = {"topk_hit", "expected", "sample_class"}
234 out_fieldnames = [f for f in fieldnames if f not in DROP_COLS] 234 out_fieldnames = [f for f in fieldnames if f not in DROP_COLS]
235 out_path = Path(args.output) 235 out_path = Path(args.output)
236 236
...@@ -294,12 +294,12 @@ def main(): ...@@ -294,12 +294,12 @@ def main():
294 for v, d in variants.items(): 294 for v, d in variants.items():
295 print(f" {v:20s} acc={d['accuracy']:.4f} n={d['total']}") 295 print(f" {v:20s} acc={d['accuracy']:.4f} n={d['total']}")
296 296
297 # 打印被移除的样本明细 297 # 打印重新标注的样本明细
298 if removed: 298 if relabeled:
299 print(f"\n移除的 {len(removed)} 条负样本:") 299 print(f"\n重新标注的 {len(relabeled)} 条(负样本→正样本):")
300 for r in removed: 300 for r in relabeled:
301 print(f" song_id={r.get('query_song_id')} variant={r.get('variant')} " 301 print(f" song_id={r.get('query_song_id')} variant={r.get('variant')} "
302 f"singer={r.get('singer_name')} correct_was={r.get('correct')}") 302 f"singer={r.get('singer_name')} correct={r.get('correct')}")
303 303
304 304
305 if __name__ == "__main__": 305 if __name__ == "__main__":
......