fix
Showing
6 changed files
with
37 additions
and
37 deletions
| ... | @@ -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__": | ... | ... |
-
Please register or sign in to post a comment