Alex W. commited on
Commit
357d754
·
1 Parent(s): 38fc6ed

add 2 UI tabs.

Browse files
Files changed (2) hide show
  1. ui/tab_database.py +233 -0
  2. ui/tab_leaderboard.py +163 -0
ui/tab_database.py ADDED
@@ -0,0 +1,233 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ui/tab_database.py
2
+ """
3
+ Tab4:数据库浏览
4
+ - 查看已分析模型列表
5
+ - 查看某模型的逐层原始数据
6
+ - 数据库统计信息
7
+ """
8
+
9
+ import gradio as gr
10
+ import pandas as pd
11
+
12
+ from db.schema import init_db, get_db_stats
13
+ from db.reader import (
14
+ get_analyzed_models,
15
+ get_model_summary,
16
+ get_layer_metrics,
17
+ get_resume_status,
18
+ )
19
+
20
+
21
+ def load_db_stats() -> str:
22
+ """获取数据库统计信息"""
23
+ conn = init_db()
24
+ stats = get_db_stats(conn)
25
+ return (
26
+ f"📊 数据库统计\n"
27
+ f"{'─'*40}\n"
28
+ f" 模型数: {stats.get('models', 0)}\n"
29
+ f" 组件数: {stats.get('components', 0)}\n"
30
+ f" 层头记录数: {stats.get('layer_head_metrics', 0)}\n"
31
+ f" 汇总行数: {stats.get('model_summary', 0)}\n"
32
+ f" 数据库大小: {stats.get('db_size_mb', 0)} MB\n"
33
+ )
34
+
35
+
36
+ def load_model_list() -> pd.DataFrame:
37
+ """加载已分析模型列表"""
38
+ conn = init_db()
39
+ df = get_analyzed_models(conn)
40
+ if df.empty:
41
+ return pd.DataFrame(
42
+ columns=["model_id", "model_type", "analyzed_at",
43
+ "analyze_sec", "n_components", "total_layers"]
44
+ )
45
+ return df
46
+
47
+
48
+ def load_model_detail(model_id: str) -> tuple[pd.DataFrame, pd.DataFrame, str]:
49
+ """
50
+ 加载模型详情
51
+ 返回 (summary_df, 断点续传状态文本)
52
+ """
53
+ if not model_id.strip():
54
+ return pd.DataFrame(), pd.DataFrame(), "请输入模型 ID"
55
+
56
+ conn = init_db()
57
+
58
+ # 汇总统计
59
+ summary_df = get_model_summary(conn, model_id.strip())
60
+
61
+ # 断点续传状态(按前缀)
62
+ status_lines = [f"📍 断点续传状态:{model_id}\n{'─'*50}\n"]
63
+ if not summary_df.empty:
64
+ for pfx in summary_df["prefix"].unique():
65
+ rs = get_resume_status(conn, model_id.strip(), pfx)
66
+ status_lines.append(
67
+ f" [{pfx}]\n"
68
+ f" 已完成层数:{rs['total_done']}\n"
69
+ f" 层号:{sorted(rs['done_layers'])}\n"
70
+ )
71
+ else:
72
+ status_lines.append(" 暂无数据\n")
73
+
74
+ return summary_df, "".join(status_lines)
75
+
76
+
77
+ def load_layer_data(
78
+ model_id: str,
79
+ prefix: str,
80
+ layer_type: str,
81
+ start_layer: int,
82
+ end_layer: int,
83
+ ) -> tuple[pd.DataFrame, str]:
84
+ """加载逐头原始数据"""
85
+ if not model_id.strip():
86
+ return pd.DataFrame(), "请输入模型 ID"
87
+
88
+ conn = init_db()
89
+ lt = layer_type if layer_type != "all" else None
90
+ pfx = prefix.strip() or None
91
+
92
+ df = get_layer_metrics(
93
+ conn,
94
+ model_id = model_id.strip(),
95
+ prefix = pfx,
96
+ layer_type = lt,
97
+ start_layer = int(start_layer),
98
+ end_layer = int(end_layer),
99
+ )
100
+
101
+ if df.empty:
102
+ return pd.DataFrame(), f"⚠️ 无数据:model={model_id} prefix={pfx} layer_type={lt}"
103
+
104
+ status = (
105
+ f"✅ {len(df)} 条记录 "
106
+ f"| 层 {df['layer'].min()}~{df['layer'].max()} "
107
+ f"| prefix={pfx or '全部'}"
108
+ )
109
+ return df, status
110
+
111
+
112
+ # ─────────────────────────────────────────────
113
+ # Tab4 UI
114
+ # ─────────────────────────────────────────────
115
+
116
+ def build_tab_database():
117
+ with gr.Tab("🗄️ 数据库"):
118
+ gr.Markdown("## 数据库浏览 \n查看已分析模型的原始数据和汇总统计。")
119
+
120
+ # ── 数据库统计 ──────────────────────────
121
+ with gr.Row():
122
+ stats_text = gr.Textbox(
123
+ label="数据库统计",
124
+ value="点击刷新",
125
+ lines=7,
126
+ interactive=False,
127
+ scale=2,
128
+ )
129
+ refresh_stats_btn = gr.Button(
130
+ "🔄 刷新统计", scale=1, variant="secondary"
131
+ )
132
+
133
+ refresh_stats_btn.click(
134
+ fn=load_db_stats,
135
+ outputs=stats_text,
136
+ )
137
+
138
+ gr.Markdown("---")
139
+
140
+ # ── 已分析模型列表 ──────────────────────
141
+ gr.Markdown("### 已分析模型")
142
+ with gr.Row():
143
+ refresh_models_btn = gr.Button(
144
+ "🔄 刷新模型列表", variant="secondary"
145
+ )
146
+
147
+ models_table = gr.Dataframe(
148
+ label="已分析模型",
149
+ interactive=False,
150
+ )
151
+
152
+ refresh_models_btn.click(
153
+ fn=load_model_list,
154
+ outputs=models_table,
155
+ )
156
+
157
+ gr.Markdown("---")
158
+
159
+ # ── 模型详情 ────────────────────────────
160
+ gr.Markdown("### 模型详情 & 断点续传状态")
161
+ with gr.Row():
162
+ detail_model_id = gr.Textbox(
163
+ label="模型 ID",
164
+ placeholder="google/gemma-4-e2b",
165
+ scale=3,
166
+ )
167
+ load_detail_btn = gr.Button(
168
+ "📋 查看详情", variant="secondary", scale=1
169
+ )
170
+
171
+ resume_status_text = gr.Textbox(
172
+ label="断点续传状态",
173
+ lines=8,
174
+ interactive=False,
175
+ )
176
+ summary_table = gr.Dataframe(
177
+ label="模型汇总统计(all/standard/global 三行)",
178
+ interactive=False,
179
+ )
180
+
181
+ load_detail_btn.click(
182
+ fn=load_model_detail,
183
+ inputs=[detail_model_id],
184
+ outputs=[summary_table, resume_status_text],
185
+ )
186
+
187
+ gr.Markdown("---")
188
+
189
+ # ── 逐头原始数据 ────────────────────────
190
+ gr.Markdown("### 逐头原始数据查询")
191
+ with gr.Row():
192
+ raw_model_id = gr.Textbox(
193
+ label="模型 ID",
194
+ placeholder="google/gemma-4-e2b",
195
+ scale=2,
196
+ )
197
+ raw_prefix = gr.Textbox(
198
+ label="组件前缀(留空=全部)",
199
+ placeholder="model.language_model.",
200
+ scale=2,
201
+ )
202
+ raw_layer_type = gr.Dropdown(
203
+ label="层类型",
204
+ choices=["all", "standard", "global"],
205
+ value="all",
206
+ scale=1,
207
+ )
208
+ with gr.Row():
209
+ raw_start = gr.Number(
210
+ label="起始层号", value=0, precision=0, scale=1
211
+ )
212
+ raw_end = gr.Number(
213
+ label="结束层号", value=10, precision=0, scale=1
214
+ )
215
+ load_raw_btn = gr.Button(
216
+ "🔍 查询数据", variant="secondary", scale=1
217
+ )
218
+
219
+ raw_status = gr.Textbox(
220
+ label="查询状态", lines=1, interactive=False
221
+ )
222
+ raw_table = gr.Dataframe(
223
+ label="逐头原始数据",
224
+ interactive=False,
225
+ wrap=False,
226
+ )
227
+
228
+ load_raw_btn.click(
229
+ fn=load_layer_data,
230
+ inputs=[raw_model_id, raw_prefix, raw_layer_type,
231
+ raw_start, raw_end],
232
+ outputs=[raw_table, raw_status],
233
+ )
ui/tab_leaderboard.py ADDED
@@ -0,0 +1,163 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ui/tab_leaderboard.py
2
+ """
3
+ Tab3:王氏评分排行榜
4
+ - 从 model_summary 读取,按 wang_score 降序
5
+ - 支持按组件过滤(language_model / vision_tower / all)
6
+ - 支持按 layer_type 过滤(standard / global / all)
7
+ """
8
+
9
+ import gradio as gr
10
+ import pandas as pd
11
+ import numpy as np
12
+
13
+ from db.schema import init_db
14
+ from db.reader import get_leaderboard
15
+
16
+
17
+ # ─────────────────────────────────────────────
18
+ # 排行榜列格式化
19
+ # ─────────────────────────────────────────────
20
+
21
+ def _format_leaderboard(df: pd.DataFrame) -> pd.DataFrame:
22
+ """格式化排行榜显示列"""
23
+ if df.empty:
24
+ return df
25
+
26
+ # 提取可读的模型名(去掉 org 前缀)
27
+ df = df.copy()
28
+ df["model_name"] = df["model_id"].apply(
29
+ lambda x: x.split("/")[-1] if "/" in x else x
30
+ )
31
+
32
+ # 王氏评分百分制(便于直觉理解)
33
+ df["wang_score_pct"] = df["wang_score"].apply(
34
+ lambda x: f"{x*100:.3f}" if pd.notna(x) else "N/A"
35
+ )
36
+
37
+ # 格式化关键指标
38
+ for col in ["median_pearson_QK", "median_ssr_QK", "mean_ssr_QK"]:
39
+ if col in df.columns:
40
+ df[col] = df[col].apply(
41
+ lambda x: f"{x:.6f}" if pd.notna(x) else "N/A"
42
+ )
43
+
44
+ # 选择展示列
45
+ display_cols = [
46
+ "model_name",
47
+ "prefix",
48
+ "layer_type",
49
+ "wang_score_pct",
50
+ "median_pearson_QK",
51
+ "median_ssr_QK",
52
+ "mean_ssr_QK",
53
+ "median_cosU_QK",
54
+ "median_cosU_QV",
55
+ "median_cosV_QK",
56
+ "n_layers",
57
+ "n_records",
58
+ "model_id", # 完整 ID 放最后
59
+ ]
60
+ existing = [c for c in display_cols if c in df.columns]
61
+ return df[existing]
62
+
63
+
64
+ def load_leaderboard(
65
+ prefix_filter: str,
66
+ layer_type: str,
67
+ ) -> tuple[pd.DataFrame, str]:
68
+ """
69
+ 加载排行榜数据
70
+ 返回 (DataFrame, 状态文本)
71
+ """
72
+ conn = init_db()
73
+
74
+ # prefix_filter 空字符串 → None(不过滤)
75
+ pfx = prefix_filter.strip() or None
76
+ lt = layer_type if layer_type != "all" else "standard"
77
+
78
+ df = get_leaderboard(conn, prefix_filter=pfx, layer_type=lt, limit=100)
79
+
80
+ if df.empty:
81
+ return pd.DataFrame(), (
82
+ "📭 排行榜暂无数据\n"
83
+ "请先在「分析」Tab 分析至少一个模型的完整层。\n"
84
+ f"(当前过滤:prefix='{pfx}', layer_type='{lt}')"
85
+ )
86
+
87
+ formatted = _format_leaderboard(df)
88
+ status = (
89
+ f"✅ 共 {len(formatted)} 条记录 "
90
+ f"| layer_type={lt} "
91
+ f"| prefix_filter='{pfx or '全部'}'"
92
+ )
93
+ return formatted, status
94
+
95
+
96
+ # ─────────────────────────────────────────────
97
+ # Tab3 UI
98
+ # ─────────────────────────────────────────────
99
+
100
+ def build_tab_leaderboard():
101
+ with gr.Tab("🏆 排行榜"):
102
+ gr.Markdown("""
103
+ ## 王氏评分排行榜
104
+ **Wang Score = 1 − median(SSR_QK)**,越高越好(理论极值 = 1)
105
+ 基于 `standard` 层计算(排除 K=V 共享的全局层干扰)。
106
+ """)
107
+
108
+ with gr.Row():
109
+ prefix_input = gr.Textbox(
110
+ label="组件过滤(含关键词即匹配,留空=全部)",
111
+ placeholder="language_model",
112
+ value="",
113
+ scale=3,
114
+ )
115
+ layer_type_input = gr.Dropdown(
116
+ label="层类型",
117
+ choices=["standard", "global", "all"],
118
+ value="standard",
119
+ scale=1,
120
+ )
121
+ refresh_btn = gr.Button("🔄 刷新排行榜", variant="primary", scale=1)
122
+
123
+ status_text = gr.Textbox(
124
+ label="状态",
125
+ value="点击「刷新排行榜」加载数据",
126
+ lines=1,
127
+ interactive=False,
128
+ )
129
+
130
+ leaderboard_table = gr.Dataframe(
131
+ label="王氏评分排行榜(按 Wang Score 降序)",
132
+ headers=[
133
+ "model_name", "prefix", "layer_type",
134
+ "wang_score_pct",
135
+ "median_pearson_QK", "median_ssr_QK", "mean_ssr_QK",
136
+ "median_cosU_QK", "median_cosU_QV", "median_cosV_QK",
137
+ "n_layers", "n_records", "model_id",
138
+ ],
139
+ interactive=False,
140
+ wrap=True,
141
+ )
142
+
143
+ gr.Markdown("""
144
+ ### 指标说明
145
+ | 指标 | 含义 | 越好 |
146
+ |------|------|------|
147
+ | Wang Score | 1 − median(SSR_QK),综合推理能力评分 | ↑ 高 |
148
+ | median_pearson_QK | Q/K 奇异值谱 Pearson 相关中位数(第一定律) | ↑ 高 |
149
+ | median_ssr_QK | Q/K 归一化谱失配中位数(第二定律) | ↓ 低 |
150
+ | median_cosU_QK | Q/K 输出子空间对齐(第四定律,≈随机正交) | ≈ 1/√d |
151
+ | median_cosU_QV | Q/V 输出子空间(第四定律,超正交) | ↓ 低 |
152
+ | median_cosV_QK | Q/K 输入子空间(第五定律,≈随机正交) | ≈ 1/√D |
153
+ """)
154
+
155
+ # 事件绑定
156
+ refresh_btn.click(
157
+ fn=load_leaderboard,
158
+ inputs=[prefix_input, layer_type_input],
159
+ outputs=[leaderboard_table, status_text],
160
+ )
161
+
162
+ # 启动时自动加载
163
+ leaderboard_table.change(fn=None)