Wendy-Fly commited on
Commit
20981bf
·
verified ·
1 Parent(s): bf13d1c

compare embedding estimated_position vs LLM positions/scores

Browse files
Files changed (1) hide show
  1. compare_positions.py +171 -0
compare_positions.py ADDED
@@ -0,0 +1,171 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """比较 csv 里多种来源的 position / score 的 F1 指标。
3
+
4
+ 读取 golden_set_with_estimated.csv(带 estimated_position 列),
5
+ 对以下 4 套 position / score 各算:
6
+ - 默认阈值 F1(rank<106 / score>=44.72)
7
+ - 扫阈值最优 F1
8
+ - AUC
9
+
10
+ 来源:
11
+ 1. estimated_position (embedding top-100 mean rank)
12
+ 2. position_gpt_4.1 / score_gpt_4.1
13
+ 3. position_gemini_2.5_flash / score_gemini_2.5_flash
14
+
15
+ 用法:
16
+ python3 compare_positions.py
17
+ python3 compare_positions.py --csv /path/to/golden_set_with_estimated.csv
18
+ """
19
+ import argparse
20
+ import json
21
+ from pathlib import Path
22
+
23
+ import numpy as np
24
+ import pandas as pd
25
+
26
+
27
+ DEFAULT_CSV = "/mnt/bn/tns-algo-ue-my/biaowu/aipf_dm_metric/example/yss_ruler_eval/data/aipf_golden_set.csv"
28
+
29
+
30
+ def metrics(preds, gts):
31
+ tp = int(((preds == 1) & (gts == 1)).sum())
32
+ fp = int(((preds == 1) & (gts == 0)).sum())
33
+ tn = int(((preds == 0) & (gts == 0)).sum())
34
+ fn = int(((preds == 0) & (gts == 1)).sum())
35
+ p = tp/(tp+fp) if tp+fp else 0.0
36
+ r = tp/(tp+fn) if tp+fn else 0.0
37
+ f = 2*p*r/(p+r) if p+r else 0.0
38
+ a = (tp+tn)/len(preds)
39
+ return tp, fp, tn, fn, p, r, f, a
40
+
41
+
42
+ def best_threshold(scores, gts):
43
+ """scores 越大越严重时调用;返回 (f1, thr, p, r)"""
44
+ valid = ~np.isnan(scores)
45
+ s, g = scores[valid], gts[valid]
46
+ cands = sorted(set(s.tolist()))
47
+ best = (-1.0, None, None, None)
48
+ for c in cands:
49
+ preds = (s >= c).astype(int)
50
+ _, _, _, _, p, r, f, _ = metrics(preds, g)
51
+ if f > best[0]:
52
+ best = (f, c, p, r)
53
+ return best
54
+
55
+
56
+ def auc(scores, gts):
57
+ valid = ~np.isnan(scores)
58
+ s, g = scores[valid], gts[valid]
59
+ if len(set(g.tolist())) < 2:
60
+ return float("nan")
61
+ try:
62
+ from sklearn.metrics import roc_auc_score
63
+ return roc_auc_score(g, s)
64
+ except ImportError:
65
+ return float("nan")
66
+
67
+
68
+ def parse_args():
69
+ p = argparse.ArgumentParser()
70
+ p.add_argument("--csv", default="golden_set_with_estimated.csv")
71
+ p.add_argument("--positive-label", default="Y")
72
+ p.add_argument("--boundary-rank", type=int, default=106)
73
+ p.add_argument("--boundary-score", type=float, default=44.72)
74
+ p.add_argument("--output-md", default="position_comparison.md")
75
+ return p.parse_args()
76
+
77
+
78
+ def main():
79
+ args = parse_args()
80
+ print(f"[1] read csv: {args.csv}")
81
+ df = pd.read_csv(args.csv, keep_default_na=True)
82
+ print(f" {len(df)} rows, cols: {len(df.columns)}")
83
+ print(f" label dist: {df['label'].value_counts().to_dict()}")
84
+
85
+ gts = df["label"].astype(str).str.upper().eq(args.positive_label.upper()).astype(int).values
86
+
87
+ # 4 套预测的"分数"(统一成"越大越严重"以便扫阈值)
88
+ sources = [] # (name, score_array, default_threshold, default_pred_fn)
89
+ if "estimated_position" in df.columns:
90
+ pos = pd.to_numeric(df["estimated_position"], errors="coerce").values
91
+ sources.append((
92
+ "embedding estimated_position",
93
+ -pos, # 取负 → 越大越严重
94
+ -args.boundary_rank, # 默认阈值也取负
95
+ "rank<106",
96
+ ))
97
+ if "position_gpt_4.1" in df.columns:
98
+ pos = pd.to_numeric(df["position_gpt_4.1"], errors="coerce").values
99
+ sources.append(("LLM gpt_4.1 position", -pos, -args.boundary_rank, "rank<106"))
100
+ if "score_gpt_4.1" in df.columns:
101
+ sc = pd.to_numeric(df["score_gpt_4.1"], errors="coerce").values
102
+ sources.append(("LLM gpt_4.1 score", sc, args.boundary_score, "score>=44.72"))
103
+ if "position_gemini_2.5_flash" in df.columns:
104
+ pos = pd.to_numeric(df["position_gemini_2.5_flash"], errors="coerce").values
105
+ sources.append(("LLM gemini_2.5_flash position", -pos, -args.boundary_rank, "rank<106"))
106
+ if "score_gemini_2.5_flash" in df.columns:
107
+ sc = pd.to_numeric(df["score_gemini_2.5_flash"], errors="coerce").values
108
+ sources.append(("LLM gemini_2.5_flash score", sc, args.boundary_score, "score>=44.72"))
109
+
110
+ if not sources:
111
+ raise SystemExit("没有可用的 position / score 列")
112
+
113
+ # ---- 算指标 ----
114
+ rows = []
115
+ for name, s, default_thr, default_desc in sources:
116
+ # 默认阈值
117
+ valid = ~np.isnan(s)
118
+ preds = np.zeros_like(s, dtype=int)
119
+ preds[valid] = (s[valid] >= default_thr).astype(int)
120
+ tp, fp, tn, fn, prec, rec, f1, acc = metrics(preds, gts)
121
+
122
+ # 扫最优
123
+ best_f1, best_thr, best_p, best_r = best_threshold(s, gts)
124
+ a = auc(s, gts)
125
+
126
+ rows.append({
127
+ "source": name,
128
+ "default": default_desc,
129
+ "n_valid": int(valid.sum()),
130
+ "TP": tp, "FP": fp, "TN": tn, "FN": fn,
131
+ "P": round(prec, 4),
132
+ "R": round(rec, 4),
133
+ "F1": round(f1, 4),
134
+ "Acc": round(acc, 4),
135
+ "best F1": round(best_f1, 4),
136
+ "best thr": best_thr,
137
+ "AUC": round(a, 4) if not np.isnan(a) else None,
138
+ })
139
+
140
+ out_df = pd.DataFrame(rows)
141
+ print("\n" + out_df.to_string(index=False))
142
+
143
+ # ---- 写 markdown ----
144
+ with open(args.output_md, "w", encoding="utf-8") as f:
145
+ f.write("# Position / Score 来源对比\n\n")
146
+ f.write(f"数据:`{args.csv}` ({len(df)} rows)\n\n")
147
+ f.write(f"GT:`label == '{args.positive_label}'` → 1\n\n")
148
+ f.write(f"默认阈值:rank<{args.boundary_rank} 或 score≥{args.boundary_score}\n\n")
149
+ f.write("## 完整对比\n\n")
150
+ f.write(out_df.to_markdown(index=False))
151
+ f.write("\n\n## 关键观察\n\n")
152
+ # F1 排名
153
+ sorted_rows = sorted(rows, key=lambda r: -r["F1"])
154
+ f.write(f"- 默认阈值下 F1 最高:**{sorted_rows[0]['source']}** ({sorted_rows[0]['F1']})\n")
155
+ f.write(f"- 默认阈值下 F1 最低:**{sorted_rows[-1]['source']}** ({sorted_rows[-1]['F1']})\n")
156
+ # AUC 排名
157
+ valid_rows = [r for r in rows if r["AUC"] is not None]
158
+ if valid_rows:
159
+ sorted_rows2 = sorted(valid_rows, key=lambda r: -r["AUC"])
160
+ f.write(f"- AUC 最高(分布可分性):**{sorted_rows2[0]['source']}** ({sorted_rows2[0]['AUC']})\n")
161
+ # embedding vs LLM gap
162
+ emb = next((r for r in rows if r["source"].startswith("embedding")), None)
163
+ gem = next((r for r in rows if "gemini" in r["source"] and "score" in r["source"]), None)
164
+ if emb and gem:
165
+ f.write(f"- embedding 估的 F1 比 Gemini 低 **{(gem['best F1']-emb['best F1']):.4f}**(best F1 对比)\n")
166
+
167
+ print(f"\n[written] {args.output_md}")
168
+
169
+
170
+ if __name__ == "__main__":
171
+ main()