Wendy-Fly commited on
Commit
1670e6a
·
verified ·
1 Parent(s): bddfa08

add full t-SNE: 1000 golden + 200 ruler

Browse files
Files changed (1) hide show
  1. full_tsne.py +157 -0
full_tsne.py ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """把 1000 golden + 200 ruler 一起做 t-SNE 可视化。
3
+
4
+ 复用 cache_emb/ 里缓存的 embedding,不重算。
5
+ 默认 1200 个点全画出来,golden 按 GT label 着色,ruler 按 score 着色。
6
+
7
+ 用法:
8
+ python3 full_tsne.py
9
+ python3 full_tsne.py --perplexity 30 --output full_tsne.png
10
+ python3 full_tsne.py --label-rulers boundary # 只标 boundary 附近的 ruler rank
11
+ python3 full_tsne.py --label-rulers all # 标全部 200 个 ruler rank(密)
12
+ python3 full_tsne.py --label-rulers none # 不标 rank(最干净)
13
+ """
14
+ import argparse
15
+ import json
16
+ from pathlib import Path
17
+
18
+ import numpy as np
19
+ import pandas as pd
20
+
21
+ import matplotlib
22
+ matplotlib.use("Agg")
23
+ import matplotlib.pyplot as plt
24
+ from sklearn.manifold import TSNE
25
+
26
+
27
+ DEFAULTS = dict(
28
+ cache_dir = "cache_emb",
29
+ csv = "/mnt/bn/tns-algo-ue-my/biaowu/aipf_dm_metric/example/yss_ruler_eval/data/aipf_golden_set.csv",
30
+ ruler = "/mnt/bn/tns-algo-ue-my/biaowu/aipf_dm_metric/ranking_moderation/data/dm/youth_sexual_and_physical_abuse_aigt_v009/ranking_bucket/ruler_items.json",
31
+ )
32
+
33
+
34
+ def load_npy_pair(cache_dir, n_csv, n_ruler, max_length=4096):
35
+ cd = Path(cache_dir)
36
+ csvs = list(cd.glob(f"csv_*_n{n_csv}_L{max_length}.npy"))
37
+ rulers = list(cd.glob(f"ruler_*_n{n_ruler}_L{max_length}.npy"))
38
+ if not csvs or not rulers:
39
+ raise FileNotFoundError(f"找不到缓存。期望 {cd}/csv_*_n{n_csv}_L{max_length}.npy 和 ruler_*_n{n_ruler}_L{max_length}.npy")
40
+ return np.load(csvs[0]), np.load(rulers[0])
41
+
42
+
43
+ def load_ruler_meta(path):
44
+ with open(path) as f:
45
+ data = json.load(f)
46
+ items = data if isinstance(data, list) else (data.get("items") or data.get("ruler_items") or data.get("data") or [])
47
+ ranks = np.array([int(it["rank"]) for it in items])
48
+ scores = np.array([float(it["score"]) for it in items])
49
+ return ranks, scores
50
+
51
+
52
+ def main():
53
+ p = argparse.ArgumentParser()
54
+ p.add_argument("--cache-dir", default=DEFAULTS["cache_dir"])
55
+ p.add_argument("--csv", default=DEFAULTS["csv"])
56
+ p.add_argument("--ruler", default=DEFAULTS["ruler"])
57
+ p.add_argument("--positive-label", default="Y")
58
+ p.add_argument("--boundary-rank", type=int, default=106)
59
+ p.add_argument("--max-length", type=int, default=4096)
60
+ p.add_argument("--perplexity", type=float, default=30.0)
61
+ p.add_argument("--output", default="full_tsne.png")
62
+ p.add_argument("--label-rulers", default="boundary",
63
+ choices=["all", "boundary", "none"],
64
+ help="all=全部标; boundary=只标 boundary±10 + 端点; none=不标")
65
+ p.add_argument("--label-fontsize", type=float, default=5)
66
+ return run(p.parse_args())
67
+
68
+
69
+ def run(args):
70
+ print("[1] load labels and ruler meta")
71
+ df = pd.read_csv(args.csv, keep_default_na=False)
72
+ gts = df["label"].astype(str).str.upper().eq(args.positive_label.upper()).astype(int).values
73
+ ruler_rank, ruler_score = load_ruler_meta(args.ruler)
74
+ n_csv, n_ruler = len(gts), len(ruler_rank)
75
+ print(f" csv={n_csv}, ruler={n_ruler}")
76
+
77
+ print("[2] load cached embeddings")
78
+ csv_emb, ruler_emb = load_npy_pair(args.cache_dir, n_csv, n_ruler, args.max_length)
79
+ print(f" csv_emb={csv_emb.shape}, ruler_emb={ruler_emb.shape}")
80
+
81
+ all_emb = np.vstack([csv_emb, ruler_emb])
82
+ print(f"[3] t-SNE on {all_emb.shape[0]} points (perplexity={args.perplexity})")
83
+ tsne = TSNE(n_components=2, perplexity=args.perplexity,
84
+ init="pca", random_state=42, metric="cosine",
85
+ learning_rate="auto")
86
+ xy = tsne.fit_transform(all_emb)
87
+ csv_xy = xy[:n_csv]
88
+ ruler_xy = xy[n_csv:]
89
+
90
+ print(f"[4] draw to {args.output}")
91
+ fig, ax = plt.subplots(figsize=(16, 13), dpi=130)
92
+
93
+ # golden A(GT=0)灰色淡
94
+ a_mask = gts == 0
95
+ ax.scatter(csv_xy[a_mask, 0], csv_xy[a_mask, 1],
96
+ c="#bbbbbb", s=12, alpha=0.55,
97
+ edgecolor="none", label=f"golden A (N={a_mask.sum()})")
98
+
99
+ # golden Y(GT=1)红色
100
+ y_mask = gts == 1
101
+ ax.scatter(csv_xy[y_mask, 0], csv_xy[y_mask, 1],
102
+ c="#d62728", s=14, alpha=0.85,
103
+ edgecolor="none", label=f"golden Y (N={y_mask.sum()})")
104
+
105
+ # ruler 200 用 colormap,按 score 着色,加黑边圈以区分
106
+ sc = ax.scatter(ruler_xy[:, 0], ruler_xy[:, 1],
107
+ c=ruler_score, cmap="RdYlGn_r",
108
+ s=70, alpha=0.95,
109
+ edgecolor="black", linewidth=0.8,
110
+ label=f"ruler (N={n_ruler})")
111
+ cbar = plt.colorbar(sc, ax=ax, shrink=0.7)
112
+ cbar.set_label("ruler score (high = severe)")
113
+
114
+ # 标 ruler rank
115
+ if args.label_rulers == "all":
116
+ for (x, y), r in zip(ruler_xy, ruler_rank):
117
+ ax.annotate(str(r), (x, y), fontsize=args.label_fontsize,
118
+ ha="center", va="center", alpha=0.9)
119
+ elif args.label_rulers == "boundary":
120
+ # 标端点 + boundary 附近
121
+ targets = set([0, n_ruler - 1])
122
+ targets.update(range(max(0, args.boundary_rank - 10),
123
+ min(n_ruler, args.boundary_rank + 11)))
124
+ for i, ((x, y), r) in enumerate(zip(ruler_xy, ruler_rank)):
125
+ if int(r) in targets:
126
+ ax.annotate(str(r), (x, y),
127
+ fontsize=args.label_fontsize + 2,
128
+ ha="center", va="center",
129
+ fontweight="bold")
130
+
131
+ ax.set_title(f"t-SNE: 1000 golden + 200 ruler (Qwen3-Embedding-8B)\n"
132
+ f"red=GT=Y, gray=GT=A, color circles=ruler items "
133
+ f"(rank={'/'.join(['0', str(args.boundary_rank), str(n_ruler-1)])} highlighted)")
134
+ ax.set_xlabel("t-SNE 1")
135
+ ax.set_ylabel("t-SNE 2")
136
+ ax.legend(loc="best", framealpha=0.9)
137
+ plt.tight_layout()
138
+ plt.savefig(args.output, dpi=130, bbox_inches="tight")
139
+ print(f" saved: {args.output}")
140
+
141
+ # 顺便算一个简单的"几何分离度"
142
+ y_centroid = csv_xy[y_mask].mean(axis=0)
143
+ a_centroid = csv_xy[a_mask].mean(axis=0)
144
+ ruler_top = ruler_xy[ruler_rank < args.boundary_rank].mean(axis=0)
145
+ ruler_bot = ruler_xy[ruler_rank >= args.boundary_rank].mean(axis=0)
146
+ print("\n[centroids in t-SNE space]")
147
+ print(f" golden Y -> {y_centroid}")
148
+ print(f" golden A -> {a_centroid}")
149
+ print(f" ruler severe -> {ruler_top}")
150
+ print(f" ruler not -> {ruler_bot}")
151
+ print(f" Y-A distance : {np.linalg.norm(y_centroid - a_centroid):.2f}")
152
+ print(f" Y-ruler-severe : {np.linalg.norm(y_centroid - ruler_top):.2f}")
153
+ print(f" A-ruler-not : {np.linalg.norm(a_centroid - ruler_bot):.2f}")
154
+
155
+
156
+ if __name__ == "__main__":
157
+ main()