Spaces:
Running
Running
Alex W. commited on
Commit ·
3b8a0de
1
Parent(s): 8d21309
update README. include all 6 UI tabs
Browse files
README.md
CHANGED
|
@@ -9,14 +9,14 @@ python_version: '3.13'
|
|
| 9 |
app_file: app.py
|
| 10 |
pinned: false
|
| 11 |
license: apache-2.0
|
| 12 |
-
short_description: 'Compute SVD of LLM Q/K/V weights directly from Hugging Face
|
| 13 |
---
|
| 14 |
|
| 15 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
| 16 |
|
| 17 |
---
|
| 18 |
# Wang's Five Laws — LLM Spectral Analyzer
|
| 19 |
-
## 完整项目文档 README.md
|
| 20 |
|
| 21 |
---
|
| 22 |
|
|
@@ -51,6 +51,7 @@ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-
|
|
| 51 |
9. [关键设计决策](#9-关键设计决策)
|
| 52 |
10. [部署说明](#10-部署说明)
|
| 53 |
11. [依赖清单](#11-依赖清单)
|
|
|
|
| 54 |
|
| 55 |
---
|
| 56 |
|
|
@@ -90,7 +91,7 @@ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-
|
|
| 90 |
```
|
| 91 |
┌─────────────────────────────────────────────────────┐
|
| 92 |
│ app.py │
|
| 93 |
-
│ 主入口,组装所有 Tab
|
| 94 |
│ 启动时调用 init_db() │
|
| 95 |
└──────┬──────────────────────────────────────────────┘
|
| 96 |
│ 调用
|
|
@@ -103,10 +104,11 @@ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-
|
|
| 103 |
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
|
| 104 |
│ │ │ │ │
|
| 105 |
│ └────────────────┼────────────────┘ │
|
| 106 |
-
│ ┌─────────────┐
|
| 107 |
-
│ │tab_database │
|
| 108 |
-
│ │数据库浏览 │
|
| 109 |
-
│ └─────────────┘
|
|
|
|
| 110 |
└──────┬──────────────────────────┬───────────────────┘
|
| 111 |
│ 调用 │ 调用
|
| 112 |
▼ ▼
|
|
@@ -121,6 +123,15 @@ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-
|
|
| 121 |
│ │ │ │
|
| 122 |
│ metrics.py │ │ SQLite 文件 │
|
| 123 |
│ 计算五定律 │ │ /data/wang_laws.db │
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 124 |
└─────────────────┘ └─────────────────────────────┘
|
| 125 |
```
|
| 126 |
|
|
@@ -139,7 +150,7 @@ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-
|
|
| 139 |
```
|
| 140 |
项目根目录/
|
| 141 |
│
|
| 142 |
-
├── app.py # 主入口:初始化DB,组装
|
| 143 |
├── requirements.txt # 依赖清单
|
| 144 |
│
|
| 145 |
├── core/ # 计算引擎(纯Python,无副作用)
|
|
@@ -148,12 +159,15 @@ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-
|
|
| 148 |
│ ├── debug.py # 调试输出工具(受config.DEBUG控制)
|
| 149 |
│ ├── fetcher.py # HTTP Range Request 读取远程权重
|
| 150 |
│ ├── layer_profile.py # 自动推断模型层结构
|
| 151 |
-
│
|
|
|
|
|
|
|
|
|
|
| 152 |
│
|
| 153 |
├── db/ # 数据持久化层
|
| 154 |
│ ├── __init__.py # 空文件
|
| 155 |
│ ├── schema.py # 建表SQL + 数据库连接
|
| 156 |
-
│ ├── writer.py # 写入分析结果 + 断点续传
|
| 157 |
│ └── reader.py # 查询排行榜、模型详情、原始数据
|
| 158 |
│
|
| 159 |
└── ui/ # Gradio 界面层
|
|
@@ -161,7 +175,9 @@ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-
|
|
| 161 |
├── tab_inspect.py # Tab1:模型结构探测
|
| 162 |
├── tab_analyze.py # Tab2:分析模型 + 写库
|
| 163 |
├── tab_leaderboard.py # Tab3:王氏评分排行榜
|
| 164 |
-
|
|
|
|
|
|
|
| 165 |
```
|
| 166 |
|
| 167 |
---
|
|
@@ -426,26 +442,43 @@ def get_db_path() -> str:
|
|
| 426 |
|
| 427 |
#### `db/writer.py`
|
| 428 |
|
| 429 |
-
| 函数
|
| 430 |
-
| ---------------------- | --------------------------------------------------- | ------------------------------------------------ |
|
| 431 |
-
| `infer_layer_type`
|
| 432 |
-
| `
|
| 433 |
-
| `
|
| 434 |
-
| `
|
| 435 |
-
| `
|
| 436 |
-
| `
|
| 437 |
-
| `
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 438 |
|
| 439 |
**`update_model_summary` 逻辑:**
|
| 440 |
```
|
| 441 |
对 layer_type in ["all", "standard", "global"]:
|
| 442 |
从 layer_head_metrics 查对应行
|
| 443 |
-
|
| 444 |
-
|
|
|
|
| 445 |
(即使写 all/global 行,wang_score 也来自 standard 层)
|
| 446 |
INSERT OR REPLACE 写入 model_summary
|
| 447 |
```
|
| 448 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 449 |
---
|
| 450 |
|
| 451 |
#### `db/reader.py`
|
|
@@ -501,7 +534,7 @@ def build_tab_inspect() -> (inspect_model_id, inspect_token):
|
|
| 501 |
**函数:**
|
| 502 |
|
| 503 |
```python
|
| 504 |
-
def run_analysis(model_id, hf_token, start_layer, end_layer, progress)
|
| 505 |
-> (str, pd.DataFrame):
|
| 506 |
"""
|
| 507 |
完整工作流程:
|
|
@@ -510,31 +543,33 @@ def run_analysis(model_id, hf_token, start_layer, end_layer, progress)
|
|
| 510 |
1. init_db() ← 获取DB连接
|
| 511 |
2. check_quantization() ← 量化检测
|
| 512 |
3. 读取 config.json
|
| 513 |
-
4.
|
| 514 |
-
|
| 515 |
-
|
|
|
|
|
|
|
|
|
|
| 516 |
7. upsert_component() for each prefix ← 写组件信息到DB
|
| 517 |
-
8. 按 start_layer~end_layer 过滤层
|
| 518 |
|
| 519 |
[断点续传检查]
|
| 520 |
-
|
| 521 |
→ done_layers: dict[prefix, set[int]]
|
| 522 |
→ 打印待分析层和已跳过层
|
| 523 |
|
| 524 |
[逐层分析循环]
|
| 525 |
for each (prefix, layer_idx) in filtered(按prefix+layer排序):
|
| 526 |
-
|
| 527 |
-
|
| 528 |
-
|
| 529 |
-
|
| 530 |
-
|
| 531 |
-
|
| 532 |
-
|
| 533 |
-
|
| 534 |
|
| 535 |
[收尾]
|
| 536 |
-
|
| 537 |
-
|
| 538 |
返回:(日志文本, 逐头结果DataFrame)
|
| 539 |
"""
|
| 540 |
|
|
@@ -544,8 +579,8 @@ def build_tab_analyze() -> (model_id_input, token_input):
|
|
| 544 |
|
| 545 |
**UI组件:**
|
| 546 |
```
|
| 547 |
-
模型ID + Token + 起始层号 + 结束层号 + 分析按钮
|
| 548 |
-
侧边栏:推荐模型列表 + 层号说明
|
| 549 |
→ 分析日志文本框(逐头详情)
|
| 550 |
→ 逐头结果表格(37列全指标)
|
| 551 |
```
|
|
@@ -566,10 +601,12 @@ def _format_leaderboard(df: pd.DataFrame) -> pd.DataFrame:
|
|
| 566 |
- 选择展示列(隐藏冗余列)
|
| 567 |
"""
|
| 568 |
|
| 569 |
-
def load_leaderboard(
|
| 570 |
"""
|
|
|
|
|
|
|
| 571 |
调用 reader.get_leaderboard()
|
| 572 |
-
|
| 573 |
layer_type="all" → 实际查 "standard"(排行榜默认用standard)
|
| 574 |
"""
|
| 575 |
|
|
@@ -600,22 +637,127 @@ def load_model_detail(model_id) -> (pd.DataFrame, str):
|
|
| 600 |
调用 get_resume_status() for each prefix → 断点续传状态文本
|
| 601 |
"""
|
| 602 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 603 |
def load_layer_data(model_id, prefix, layer_type, start_layer, end_layer)
|
| 604 |
-> (pd.DataFrame, str):
|
| 605 |
"""调用 get_layer_metrics(),返回逐头原始数据"""
|
| 606 |
|
| 607 |
def build_tab_database():
|
| 608 |
"""
|
| 609 |
-
UI分为
|
| 610 |
1. 数据库统计(行数+文件大小)
|
| 611 |
2. 已分析模型列表
|
| 612 |
-
3. 模型
|
| 613 |
-
|
|
|
|
|
|
|
| 614 |
"""
|
| 615 |
```
|
| 616 |
|
| 617 |
---
|
| 618 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 619 |
### 5.4 `app.py`——主入口
|
| 620 |
|
| 621 |
```python
|
|
@@ -624,13 +766,15 @@ init_db() # 建表,幂等
|
|
| 624 |
|
| 625 |
# Gradio Blocks
|
| 626 |
with gr.Blocks(...) as demo:
|
| 627 |
-
# 标题 + 五定律表格
|
| 628 |
|
| 629 |
with gr.Tabs():
|
| 630 |
inspect_model_id, inspect_token = build_tab_inspect()
|
| 631 |
analyze_model_id, analyze_token = build_tab_analyze()
|
| 632 |
build_tab_leaderboard()
|
| 633 |
build_tab_database()
|
|
|
|
|
|
|
| 634 |
|
| 635 |
# Tab1 → Tab2 联动(避免重复输入)
|
| 636 |
inspect_model_id.change(fn=lambda x:x,
|
|
@@ -834,9 +978,9 @@ app.py
|
|
| 834 |
│ ├── init_db() [db/schema.py]
|
| 835 |
│ ├── check_quantization() [core/fetcher.py]
|
| 836 |
│ ├── extract_config_params() [core/layer_profile.py]
|
| 837 |
-
│ ├──
|
| 838 |
-
│ ├── load_all_shard_headers() [core/fetcher.py]
|
| 839 |
│ ├── scan_model_structure() [core/layer_profile.py]
|
|
|
|
| 840 |
│ ├── upsert_component() [db/writer.py]
|
| 841 |
│ ├── get_analyzed_layers() [db/writer.py]
|
| 842 |
│ ├── load_tensor_remote() ×3 [core/fetcher.py]
|
|
@@ -851,25 +995,61 @@ app.py
|
|
| 851 |
│ ├── write_layer_records() [db/writer.py]
|
| 852 |
│ │ └── infer_layer_type()
|
| 853 |
│ ├── update_model_summary() [db/writer.py]
|
|
|
|
| 854 |
│ │ └── _calc_summary_row()
|
| 855 |
│ └── summarize_records() [core/metrics.py]
|
| 856 |
│
|
| 857 |
├── build_tab_leaderboard() [ui/tab_leaderboard.py]
|
| 858 |
│ └── load_leaderboard()
|
| 859 |
│ ├── init_db() [db/schema.py]
|
|
|
|
|
|
|
| 860 |
│ ├── get_leaderboard() [db/reader.py]
|
| 861 |
│ └── _format_leaderboard()
|
| 862 |
│
|
| 863 |
-
|
| 864 |
-
|
| 865 |
-
|
| 866 |
-
|
| 867 |
-
|
| 868 |
-
|
| 869 |
-
|
| 870 |
-
|
| 871 |
-
|
| 872 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 873 |
```
|
| 874 |
|
| 875 |
---
|
|
@@ -905,6 +1085,28 @@ Gemma-4-31B 每6层有一个全局层,V权重不存在(K和V共享)。
|
|
| 905 |
排行榜以 `(model_id, prefix)` 为单位,
|
| 906 |
多模态模型(如Gemma-4)的language_model和vision_tower分别占一行。
|
| 907 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 908 |
---
|
| 909 |
|
| 910 |
## 10. 部署说明
|
|
@@ -1004,6 +1206,8 @@ numpy # 数值计算(统计汇总)
|
|
| 1004 |
scipy # spearman相关系数
|
| 1005 |
torch # SVD分解(torch.linalg.svd)
|
| 1006 |
huggingface_hub # list_repo_files(文件列表)
|
|
|
|
|
|
|
| 1007 |
```
|
| 1008 |
|
| 1009 |
Python 内置(无需安装):
|
|
@@ -1015,3 +1219,41 @@ re # 正则提取层号
|
|
| 1015 |
datetime # 时间戳
|
| 1016 |
dataclasses # LayerProfile数据结构
|
| 1017 |
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
app_file: app.py
|
| 10 |
pinned: false
|
| 11 |
license: apache-2.0
|
| 12 |
+
short_description: 'Compute SVD of LLM Q/K/V weights directly from Hugging Face'
|
| 13 |
---
|
| 14 |
|
| 15 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
| 16 |
|
| 17 |
---
|
| 18 |
# Wang's Five Laws — LLM Spectral Analyzer
|
| 19 |
+
## 完整项目文档 README.md(6-Tab Gradio App)
|
| 20 |
|
| 21 |
---
|
| 22 |
|
|
|
|
| 51 |
9. [关键设计决策](#9-关键设计决策)
|
| 52 |
10. [部署说明](#10-部署说明)
|
| 53 |
11. [依赖清单](#11-依赖清单)
|
| 54 |
+
12. [改动历史](#12-改动历史)
|
| 55 |
|
| 56 |
---
|
| 57 |
|
|
|
|
| 91 |
```
|
| 92 |
┌─────────────────────────────────────────────────────┐
|
| 93 |
│ app.py │
|
| 94 |
+
│ 主入口,组装所有 6 个 Tab │
|
| 95 |
│ 启动时调用 init_db() │
|
| 96 |
└──────┬──────────────────────────────────────────────┘
|
| 97 |
│ 调用
|
|
|
|
| 104 |
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
|
| 105 |
│ │ │ │ │
|
| 106 |
│ └────────────────┼────────────────┘ │
|
| 107 |
+
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
| 108 |
+
│ │tab_database │ │ tab_plot │ │ tab_tables │ │
|
| 109 |
+
│ │数据库浏览 │ │ 作图导出 │ │ 论文表格 │ │
|
| 110 |
+
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
|
| 111 |
+
│ └────────────────┼────────────────┘ │
|
| 112 |
└──────┬──────────────────────────┬───────────────────┘
|
| 113 |
│ 调用 │ 调用
|
| 114 |
▼ ▼
|
|
|
|
| 123 |
│ │ │ │
|
| 124 |
│ metrics.py │ │ SQLite 文件 │
|
| 125 |
│ 计算五定律 │ │ /data/wang_laws.db │
|
| 126 |
+
│ │ │ │
|
| 127 |
+
│ plotter.py │ │ │
|
| 128 |
+
│ matplotlib静态图 │ │ │
|
| 129 |
+
│ │ │ │
|
| 130 |
+
│ plotter_plotly │ │ │
|
| 131 |
+
│ Plotly交互图 │ │ │
|
| 132 |
+
│ │ │ │
|
| 133 |
+
│ table_gen.py │ │ │
|
| 134 |
+
│ 论文表格生成 │ │ │
|
| 135 |
└─────────────────┘ └─────────────────────────────┘
|
| 136 |
```
|
| 137 |
|
|
|
|
| 150 |
```
|
| 151 |
项目根目录/
|
| 152 |
│
|
| 153 |
+
├── app.py # 主入口:初始化DB,组装6个Tab
|
| 154 |
├── requirements.txt # 依赖清单
|
| 155 |
│
|
| 156 |
├── core/ # 计算引擎(纯Python,无副作用)
|
|
|
|
| 159 |
│ ├── debug.py # 调试输出工具(受config.DEBUG控制)
|
| 160 |
│ ├── fetcher.py # HTTP Range Request 读取远程权重
|
| 161 |
│ ├── layer_profile.py # 自动推断模型层结构
|
| 162 |
+
│ ├── metrics.py # 计算王氏五定律全部指标
|
| 163 |
+
│ ├── plotter.py # matplotlib 静态图(4×3,18×20in,300dpi)
|
| 164 |
+
│ ├── plotter_plotly.py # Plotly 原生交互图(12×1,全宽)
|
| 165 |
+
│ └── table_gen.py # 论文表格生成(6张表,LaTeX/Markdown/CSV)
|
| 166 |
│
|
| 167 |
├── db/ # 数据持久化层
|
| 168 |
│ ├── __init__.py # 空文件
|
| 169 |
│ ├── schema.py # 建表SQL + 数据库连接
|
| 170 |
+
│ ├── writer.py # 写入分析结果 + 断点续传 + 级联删除
|
| 171 |
│ └── reader.py # 查询排行榜、模型详情、原始数据
|
| 172 |
│
|
| 173 |
└── ui/ # Gradio 界面层
|
|
|
|
| 175 |
├── tab_inspect.py # Tab1:模型结构探测
|
| 176 |
├── tab_analyze.py # Tab2:分析模型 + 写库
|
| 177 |
├── tab_leaderboard.py # Tab3:王氏评分排行榜
|
| 178 |
+
├── tab_database.py # Tab4:数据库浏览 + 模型删除
|
| 179 |
+
├── tab_plot.py # Tab5:作图(Plotly交互 + matplotlib导出)
|
| 180 |
+
└── tab_tables.py # Tab6:论文表格生成
|
| 181 |
```
|
| 182 |
|
| 183 |
---
|
|
|
|
| 442 |
|
| 443 |
#### `db/writer.py`
|
| 444 |
|
| 445 |
+
| 函数 | 签名 | 用途 |
|
| 446 |
+
| ------------------------ | --------------------------------------------------- | ------------------------------------------------------------------------- |
|
| 447 |
+
| `infer_layer_type` | `(kv_shared: bool)` | `True→"global"`, `False→"standard"` |
|
| 448 |
+
| `infer_modality` | `(prefix: str)` | 从 prefix 推断模态:language/vision/audio |
|
| 449 |
+
| `check_write_permission` | `(admin_token: str)` | 验证 WRITE_TOKEN,返回 bool |
|
| 450 |
+
| `get_analyzed_layers` | `(conn, model_id, prefix)` | 返回已完成的层号集合(断点续传用) |
|
| 451 |
+
| `is_layer_complete` | `(conn, model_id, prefix, layer, expected_records)` | 检查某层记录数是否达到预期 |
|
| 452 |
+
| `upsert_model` | `(conn, model_id, model_type, notes)` | 写入/更新模型元数据 |
|
| 453 |
+
| `upsert_component` | `(conn, model_id, prefix, n_layers, ...)` | 写入/更新组件信息 |
|
| 454 |
+
| `write_layer_records` | `(conn, model_id, records: list[dict])` | 批量写入一层的逐头数据(INSERT OR REPLACE) |
|
| 455 |
+
| `_pseudobulk_col` | `(rows, col_name: str)` | Pseudo-bulk 两步聚合:消除 GQA 伪重复计数 |
|
| 456 |
+
| `_calc_summary_row` | `(rows, model_id, prefix, layer_type)` | 用 pseudo-bulk 计算单行汇总统计 |
|
| 457 |
+
| `update_model_summary` | `(conn, model_id, prefix)` | 重算并写入 model_summary 的 all/standard/global 三行 |
|
| 458 |
+
| `refresh_all_summaries` | `(conn)` | 遍历所有(model_id, prefix)重跑 update_model_summary,供 Tab3 Refresh 调用 |
|
| 459 |
+
| `delete_model` | `(conn, model_id, admin_token)` | 级联删除模型所有数据,需 WRITE_TOKEN 验证 |
|
| 460 |
|
| 461 |
**`update_model_summary` 逻辑:**
|
| 462 |
```
|
| 463 |
对 layer_type in ["all", "standard", "global"]:
|
| 464 |
从 layer_head_metrics 查对应行
|
| 465 |
+
用 _pseudobulk_col() 两步聚合(先按 kv_head 组内 median,再跨组 median)
|
| 466 |
+
→ 消除 GQA 模型(如 LLaMA-3 32Q/8KV)的伪重复计数偏差
|
| 467 |
+
wang_score 统一用 standard 层的 pseudo-bulk median(ssr_QK) 计算
|
| 468 |
(即使写 all/global 行,wang_score 也来自 standard 层)
|
| 469 |
INSERT OR REPLACE 写入 model_summary
|
| 470 |
```
|
| 471 |
|
| 472 |
+
**`delete_model` 逻辑:**
|
| 473 |
+
```
|
| 474 |
+
1. check_write_permission(admin_token) → 失败直接返回错误
|
| 475 |
+
2. 查 models 表确认模型存在
|
| 476 |
+
3. 统计各子表行数(用于返回日志)
|
| 477 |
+
4. 按顺序级联删除:
|
| 478 |
+
layer_head_metrics → model_summary → components → models
|
| 479 |
+
5. 返回 (True, 详细删除日志)
|
| 480 |
+
```
|
| 481 |
+
|
| 482 |
---
|
| 483 |
|
| 484 |
#### `db/reader.py`
|
|
|
|
| 534 |
**函数:**
|
| 535 |
|
| 536 |
```python
|
| 537 |
+
def run_analysis(model_id, hf_token, start_layer, end_layer, admin_token, progress)
|
| 538 |
-> (str, pd.DataFrame):
|
| 539 |
"""
|
| 540 |
完整工作流程:
|
|
|
|
| 543 |
1. init_db() ← 获取DB连接
|
| 544 |
2. check_quantization() ← 量化检测
|
| 545 |
3. 读取 config.json
|
| 546 |
+
4. load_all_shard_headers() ← 读所有分片header
|
| 547 |
+
(404/网络错误 → 提前返回,DB零污染)
|
| 548 |
+
5. scan_model_structure() ← 构建LayerProfile字典
|
| 549 |
+
6. upsert_model() ← 写模型元数据到DB
|
| 550 |
+
(注意:故意在 shard headers 加载成功后才写,
|
| 551 |
+
防止模型名拼写错误产生脏数据)
|
| 552 |
7. upsert_component() for each prefix ← 写组件信息到DB
|
|
|
|
| 553 |
|
| 554 |
[断点续传检查]
|
| 555 |
+
8. get_analyzed_layers() for each prefix
|
| 556 |
→ done_layers: dict[prefix, set[int]]
|
| 557 |
→ 打印待分析层和已跳过层
|
| 558 |
|
| 559 |
[逐层分析循环]
|
| 560 |
for each (prefix, layer_idx) in filtered(按prefix+layer排序):
|
| 561 |
+
9. 检查:layer_idx in done_layers[prefix] → continue(跳过)
|
| 562 |
+
10. load_tensor_remote(Q) ← HTTP Range Request
|
| 563 |
+
11. load_tensor_remote(K)
|
| 564 |
+
12. kv_shared ? W_v=W_k.clone() : load_tensor_remote(V)
|
| 565 |
+
13. analyze_layer(W_q, W_k, W_v, prof) ← 计算五定律
|
| 566 |
+
14. write_layer_records(conn, model_id, records) ← 写DB
|
| 567 |
+
15. update_model_summary(conn, model_id, prefix) ← 更新排行榜
|
| 568 |
+
16. del W_q, W_k, W_v ← 释放内存
|
| 569 |
|
| 570 |
[收尾]
|
| 571 |
+
17. 更新 models.analyze_sec(总耗时)
|
| 572 |
+
18. summarize_records() ← 生成汇总文本
|
| 573 |
返回:(日志文本, 逐头结果DataFrame)
|
| 574 |
"""
|
| 575 |
|
|
|
|
| 579 |
|
| 580 |
**UI组件:**
|
| 581 |
```
|
| 582 |
+
模型ID + Token + 起始层号 + 结束层号 + Admin Write Token + 分析按钮
|
| 583 |
+
侧边栏:推荐模型列表 + 层号说明 + Reviewer Note(留空token可只读分析)
|
| 584 |
→ 分析日志文本框(逐头详情)
|
| 585 |
→ 逐头结果表格(37列全指标)
|
| 586 |
```
|
|
|
|
| 601 |
- 选择展示列(隐藏冗余列)
|
| 602 |
"""
|
| 603 |
|
| 604 |
+
def load_leaderboard(modality, layer_type) -> (pd.DataFrame, str):
|
| 605 |
"""
|
| 606 |
+
调用 refresh_all_summaries(conn) 静默重算所有模型汇总
|
| 607 |
+
→ 自动将历史数据迁移到 pseudo-bulk 聚合
|
| 608 |
调用 reader.get_leaderboard()
|
| 609 |
+
modality 控制按模态过���(language/vision/audio/all)
|
| 610 |
layer_type="all" → 实际查 "standard"(排行榜默认用standard)
|
| 611 |
"""
|
| 612 |
|
|
|
|
| 637 |
调用 get_resume_status() for each prefix → 断点续传状态文本
|
| 638 |
"""
|
| 639 |
|
| 640 |
+
def run_delete_model(model_id, admin_token) -> (str, pd.DataFrame):
|
| 641 |
+
"""
|
| 642 |
+
调用 db/writer.delete_model() 执行级联删除
|
| 643 |
+
需要 Admin Write Token 验证
|
| 644 |
+
删除成功后自动刷新 models_table
|
| 645 |
+
返回 (状态文本, 刷新后的模型列表DataFrame)
|
| 646 |
+
"""
|
| 647 |
+
|
| 648 |
def load_layer_data(model_id, prefix, layer_type, start_layer, end_layer)
|
| 649 |
-> (pd.DataFrame, str):
|
| 650 |
"""调用 get_layer_metrics(),返回逐头原始数据"""
|
| 651 |
|
| 652 |
def build_tab_database():
|
| 653 |
"""
|
| 654 |
+
UI分为5个区块:
|
| 655 |
1. 数据库统计(行数+文件大小)
|
| 656 |
2. 已分析模型列表
|
| 657 |
+
3. 🗑️ 删除模型(Model ID + Admin Token + Delete按钮,variant="stop"红色警示)
|
| 658 |
+
→ 删除成功后自动刷新模型列表
|
| 659 |
+
4. 模型详情+断点续传状态
|
| 660 |
+
5. 逐头原始数据查询(支持按modality/layer_type/层号范围过滤)
|
| 661 |
"""
|
| 662 |
```
|
| 663 |
|
| 664 |
---
|
| 665 |
|
| 666 |
+
#### `ui/tab_plot.py` — Tab5:作图
|
| 667 |
+
|
| 668 |
+
**两条独立渲染路径(无嵌套 gr.Tabs(),用两个并排按钮区分):**
|
| 669 |
+
|
| 670 |
+
| 按钮 | 引擎 | 速度 | 输出 |
|
| 671 |
+
| ------------- | ------------------------ | ---- | ---------------------------- |
|
| 672 |
+
| ⚡ Interactive | `core/plotter_plotly.py` | ~2s | 浏览器内交互,hover/zoom |
|
| 673 |
+
| 🖨️ Export | `core/plotter.py` | ~30s | PNG(300dpi) + PDF + SVG 下载 |
|
| 674 |
+
|
| 675 |
+
**函数:**
|
| 676 |
+
|
| 677 |
+
```python
|
| 678 |
+
def gen_single_plotly(model_id, modality, start_l, end_l, show_band) -> (go.Figure, str):
|
| 679 |
+
"""从DB加载数据,调用 plotly_single(),返回 Plotly Figure"""
|
| 680 |
+
|
| 681 |
+
def gen_single_export(model_id, modality, start_l, end_l, show_band) -> (str, img, png, pdf, svg, zip):
|
| 682 |
+
"""从DB加载数据,调用 plot_single_model(),保存 PNG/PDF/SVG,返回下载链接"""
|
| 683 |
+
|
| 684 |
+
def gen_compare_plotly(model_a, model_b, modality, start_l, end_l, show_band, show_delta) -> (go.Figure, str):
|
| 685 |
+
"""双模型对比,调用 plotly_compare()"""
|
| 686 |
+
|
| 687 |
+
def gen_compare_export(model_a, model_b, modality, start_l, end_l, show_band, show_delta) -> (str, img, png, pdf, svg, zip):
|
| 688 |
+
"""双模型对比导出,调用 plot_compare_models()"""
|
| 689 |
+
|
| 690 |
+
def build_tab_plot():
|
| 691 |
+
"""
|
| 692 |
+
UI分为两个 Accordion:
|
| 693 |
+
1. 📊 Single Model:选模型 → ⚡Interactive / 🖨️Export
|
| 694 |
+
2. 📊 Two-Model Comparison:选A+B → ⚡Interactive / 🖨️Export + Δ填充开关
|
| 695 |
+
共享控件:Modality / Start Layer / End Layer / IQR band 开关
|
| 696 |
+
"""
|
| 697 |
+
```
|
| 698 |
+
|
| 699 |
+
**12子图布局(Plotly 12×1 全宽):**
|
| 700 |
+
```
|
| 701 |
+
行 0:pearson_QK 定律1 谱线性对齐
|
| 702 |
+
行 1:ssr_QK 定律2 谱形状保真度
|
| 703 |
+
行 2:alpha_QK 定律1+2 尺度因子α
|
| 704 |
+
行 3:sigma_max_Q 定律3 最大奇异值(Q)
|
| 705 |
+
行 4:sigma_max_K 定律3 最大奇异值(K)
|
| 706 |
+
行 5:cond_Q + cond_K 定律3 条件数κ(双线,对数坐标)
|
| 707 |
+
行 6:cosU_QK 定律4 输出子空间 Q-K
|
| 708 |
+
行 7:cosU_QV 定律4 输出子空间 Q-V(超正交)
|
| 709 |
+
行 8:cosU_KV 定律4 输出子空间 K-V(超正交)
|
| 710 |
+
行 9:cosV_QK 定律5 输入子空间 Q-K
|
| 711 |
+
行10:cosV_QV 定律5 输入子空间 Q-V
|
| 712 |
+
行11:cosV_KV 定律5 输入子空间 K-V
|
| 713 |
+
```
|
| 714 |
+
|
| 715 |
+
---
|
| 716 |
+
|
| 717 |
+
#### `ui/tab_tables.py` — Tab6:论文表格
|
| 718 |
+
|
| 719 |
+
**一键生成6张论文表格,数据来源:language modality + standard layers only。**
|
| 720 |
+
|
| 721 |
+
**函数:**
|
| 722 |
+
|
| 723 |
+
```python
|
| 724 |
+
def generate_tables(selected_models, table2_model_a, table2_model_b, group_text)
|
| 725 |
+
-> (status, t1~t6 DataFrames, latex_str, md_str, csv×6, latex_file, md_file, zip):
|
| 726 |
+
"""
|
| 727 |
+
工作流程:
|
| 728 |
+
1. _load_all_models(selected_models) ← 从DB读取所有选中模型数据
|
| 729 |
+
2. _parse_groups(group_text) ← 解析用户定义的层组(如"0-11,12-23")
|
| 730 |
+
3. generate_all_tables() ← core/table_gen.py 生成6张表
|
| 731 |
+
4. format_all_latex() / format_all_markdown() ← 格式化输出
|
| 732 |
+
5. 保存 CSV × 6 + .tex + .md → 打包 ZIP
|
| 733 |
+
返回:所有输出供 Gradio 组件展示和下载
|
| 734 |
+
"""
|
| 735 |
+
|
| 736 |
+
def build_tab_tables():
|
| 737 |
+
"""
|
| 738 |
+
UI分为:
|
| 739 |
+
- 模型多选框(CheckboxGroup)+ Refresh按钮
|
| 740 |
+
- Table2专用:Model A / Model B 下拉 + 层组输入框
|
| 741 |
+
- 🚀 Generate All Tables 按钮
|
| 742 |
+
- 6个 Accordion,每个内含 DataFrame + CSV下载
|
| 743 |
+
- LaTeX / Markdown 代码框(可直接复制粘贴)
|
| 744 |
+
- 批量下载:.tex / .md / ZIP
|
| 745 |
+
"""
|
| 746 |
+
```
|
| 747 |
+
|
| 748 |
+
**6张表说明:**
|
| 749 |
+
|
| 750 |
+
| 表格 | 内容 | 对应定律 |
|
| 751 |
+
| ------- | ------------------------------------------ | --------- |
|
| 752 |
+
| Table 1 | 跨模型汇总:Pearson r, SSR | 定律1 & 2 |
|
| 753 |
+
| Table 2 | SSR 层组趋势(RL改善效果,用户自定义层组) | 定律2 |
|
| 754 |
+
| Table 3 | 输出子空间 cosU:Q-K, Q-V, K-V + 随机基线 | 定律4 |
|
| 755 |
+
| Table 4 | 输入子空间 cosV:Q-K, Q-V, K-V + 随机基线 | 定律5 |
|
| 756 |
+
| Table 5 | 条件数κ:全层/第0层/深层 分别统计 | 定律3 |
|
| 757 |
+
| Table 6 | Wang Score 排行榜(按分降序) | 定律1 & 2 |
|
| 758 |
+
|
| 759 |
+
---
|
| 760 |
+
|
| 761 |
### 5.4 `app.py`——主入口
|
| 762 |
|
| 763 |
```python
|
|
|
|
| 766 |
|
| 767 |
# Gradio Blocks
|
| 768 |
with gr.Blocks(...) as demo:
|
| 769 |
+
# 标题 + 五定律表格(英中双语并排)+ DOI徽章
|
| 770 |
|
| 771 |
with gr.Tabs():
|
| 772 |
inspect_model_id, inspect_token = build_tab_inspect()
|
| 773 |
analyze_model_id, analyze_token = build_tab_analyze()
|
| 774 |
build_tab_leaderboard()
|
| 775 |
build_tab_database()
|
| 776 |
+
build_tab_plot()
|
| 777 |
+
build_tab_tables()
|
| 778 |
|
| 779 |
# Tab1 → Tab2 联动(避免重复输入)
|
| 780 |
inspect_model_id.change(fn=lambda x:x,
|
|
|
|
| 978 |
│ ├── init_db() [db/schema.py]
|
| 979 |
│ ├── check_quantization() [core/fetcher.py]
|
| 980 |
│ ├── extract_config_params() [core/layer_profile.py]
|
| 981 |
+
│ ├── load_all_shard_headers() [core/fetcher.py] ← 成功后才写DB
|
|
|
|
| 982 |
│ ├── scan_model_structure() [core/layer_profile.py]
|
| 983 |
+
│ ├── upsert_model() [db/writer.py] ← 在此之后写入
|
| 984 |
│ ├── upsert_component() [db/writer.py]
|
| 985 |
│ ├── get_analyzed_layers() [db/writer.py]
|
| 986 |
│ ├── load_tensor_remote() ×3 [core/fetcher.py]
|
|
|
|
| 995 |
│ ├── write_layer_records() [db/writer.py]
|
| 996 |
│ │ └── infer_layer_type()
|
| 997 |
│ ├── update_model_summary() [db/writer.py]
|
| 998 |
+
│ │ ├── _pseudobulk_col()
|
| 999 |
│ │ └── _calc_summary_row()
|
| 1000 |
│ └── summarize_records() [core/metrics.py]
|
| 1001 |
│
|
| 1002 |
├── build_tab_leaderboard() [ui/tab_leaderboard.py]
|
| 1003 |
│ └── load_leaderboard()
|
| 1004 |
│ ├── init_db() [db/schema.py]
|
| 1005 |
+
│ ├── refresh_all_summaries() [db/writer.py]
|
| 1006 |
+
│ │ └── update_model_summary() ×N
|
| 1007 |
│ ├── get_leaderboard() [db/reader.py]
|
| 1008 |
│ └── _format_leaderboard()
|
| 1009 |
│
|
| 1010 |
+
├── build_tab_database() [ui/tab_database.py]
|
| 1011 |
+
│ ├── load_db_stats()
|
| 1012 |
+
│ │ └── get_db_stats() [db/schema.py]
|
| 1013 |
+
│ ├── load_model_list()
|
| 1014 |
+
│ │ └── get_analyzed_models() [db/reader.py]
|
| 1015 |
+
│ ├── run_delete_model()
|
| 1016 |
+
│ │ ├── delete_model() [db/writer.py]
|
| 1017 |
+
│ │ │ └── check_write_permission()
|
| 1018 |
+
│ │ └── load_model_list() ← 删除后自动刷新
|
| 1019 |
+
│ ├── load_model_detail()
|
| 1020 |
+
│ │ ├── get_model_summary() [db/reader.py]
|
| 1021 |
+
│ │ └── get_resume_status() [db/reader.py]
|
| 1022 |
+
│ └── load_layer_data()
|
| 1023 |
+
│ └── get_layer_metrics() [db/reader.py]
|
| 1024 |
+
│
|
| 1025 |
+
├── build_tab_plot() [ui/tab_plot.py]
|
| 1026 |
+
│ ├── gen_single_plotly()
|
| 1027 |
+
│ │ ├── get_layer_metrics() [db/reader.py]
|
| 1028 |
+
│ │ └── plotly_single() [core/plotter_plotly.py]
|
| 1029 |
+
│ ├── gen_single_export()
|
| 1030 |
+
│ │ ├── get_layer_metrics() [db/reader.py]
|
| 1031 |
+
│ │ ├── plot_single_model() [core/plotter.py]
|
| 1032 |
+
│ │ └── save_figure()
|
| 1033 |
+
│ ├── gen_compare_plotly()
|
| 1034 |
+
│ │ ├── get_layer_metrics() ×2 [db/reader.py]
|
| 1035 |
+
│ │ └── plotly_compare() [core/plotter_plotly.py]
|
| 1036 |
+
│ └── gen_compare_export()
|
| 1037 |
+
│ ├── get_layer_metrics() ×2 [db/reader.py]
|
| 1038 |
+
│ ├── plot_compare_models() [core/plotter.py]
|
| 1039 |
+
│ └── save_figure()
|
| 1040 |
+
│
|
| 1041 |
+
└── build_tab_tables() [ui/tab_tables.py]
|
| 1042 |
+
└── generate_tables()
|
| 1043 |
+
├── get_layer_metrics() ×N [db/reader.py]
|
| 1044 |
+
├── generate_all_tables() [core/table_gen.py]
|
| 1045 |
+
│ ├── make_table1()
|
| 1046 |
+
│ ├── make_table2()
|
| 1047 |
+
│ ├── make_table3()
|
| 1048 |
+
│ ├── make_table4()
|
| 1049 |
+
│ ├── make_table5()
|
| 1050 |
+
│ └── make_table6()
|
| 1051 |
+
├── format_all_latex()
|
| 1052 |
+
└── format_all_markdown()
|
| 1053 |
```
|
| 1054 |
|
| 1055 |
---
|
|
|
|
| 1085 |
排行榜以 `(model_id, prefix)` 为单位,
|
| 1086 |
多模态模型(如Gemma-4)的language_model和vision_tower分别占一行。
|
| 1087 |
|
| 1088 |
+
### 防脏数据写入(Lazy Write)
|
| 1089 |
+
`upsert_model()` 和 `upsert_component()` 故意推迟到 `load_all_shard_headers()` 成功之后才调用。
|
| 1090 |
+
模型名拼写错误(如 "Meta-Llama-3-70B-intruct" 少一个s)会在 HF 返回 404 时提前 return,
|
| 1091 |
+
DB 中零污染。旧版本在量化检测通过后立即写入,会留下只有名字没有数据的孤立行,污染 Tab4/5/6。
|
| 1092 |
+
|
| 1093 |
+
### Pseudo-bulk 两步聚合(GQA 伪重复问题)
|
| 1094 |
+
GQA 模型(如 LLaMA-3-8B 32Q/8KV)中,同一 KV head 下的多个 Q head 共享同一 K,
|
| 1095 |
+
彼此强相关。若直接对所有头做 median,等价于对 KV head 的指标重复计数 group 次(伪重复)。
|
| 1096 |
+
标准做法(Nature Comms 2021):
|
| 1097 |
+
```
|
| 1098 |
+
Step 1: groupby(layer, kv_head).median() → 每KV head一个值,消除组内相关
|
| 1099 |
+
Step 2: 对Step1结果做 median/mean → 每层一个无偏代表值
|
| 1100 |
+
```
|
| 1101 |
+
实现在 `db/writer._pseudobulk_col()` 和 `core/plotter._aggregate_by_layer()`。
|
| 1102 |
+
Tab3 Refresh 按钮触发 `refresh_all_summaries()` 自动将历史数据重算为 pseudo-bulk。
|
| 1103 |
+
|
| 1104 |
+
### 级联删除与写入权限统一验证
|
| 1105 |
+
`delete_model()` 和所有写库操作均通过同一个 `check_write_permission(admin_token)` 验证,
|
| 1106 |
+
后者对比环境变量 `WRITE_TOKEN`(HF Space Secrets 注入)。
|
| 1107 |
+
删除顺序严格遵循外键依赖:`layer_head_metrics → model_summary → components → models`。
|
| 1108 |
+
删除后返回各表实际删除行数,便于审计确认。
|
| 1109 |
+
|
| 1110 |
---
|
| 1111 |
|
| 1112 |
## 10. 部署说明
|
|
|
|
| 1206 |
scipy # spearman相关系数
|
| 1207 |
torch # SVD分解(torch.linalg.svd)
|
| 1208 |
huggingface_hub # list_repo_files(文件列表)
|
| 1209 |
+
matplotlib # 静态图导出(PNG/PDF/SVG,300dpi,论文级)
|
| 1210 |
+
plotly # 交互图(12×1全宽,浏览器内hover/zoom)
|
| 1211 |
```
|
| 1212 |
|
| 1213 |
Python 内置(无需安装):
|
|
|
|
| 1219 |
datetime # 时间戳
|
| 1220 |
dataclasses # LayerProfile数据结构
|
| 1221 |
```
|
| 1222 |
+
|
| 1223 |
+
---
|
| 1224 |
+
|
| 1225 |
+
## 12. 改动历史
|
| 1226 |
+
|
| 1227 |
+
### v0.1 — 初始版本(Tab1~4)
|
| 1228 |
+
- Tab1 Inspect:模型结构探测,零下载,仅读 safetensors header
|
| 1229 |
+
- Tab2 Analyze:HTTP Range Request 逐层分析,写库,断点续传
|
| 1230 |
+
- Tab3 Leaderboard:Wang Score 排行榜
|
| 1231 |
+
- Tab4 Database:数据库浏览,逐头原始数据查询
|
| 1232 |
+
|
| 1233 |
+
### v0.2 — 作图与论文表格(Tab5~6)
|
| 1234 |
+
- 新增 `core/plotter.py`:matplotlib 4×3 静态图,18×20in @ 300dpi
|
| 1235 |
+
- 新增 `core/plotter_plotly.py`:原生 Plotly 12×1 交互图,全宽自适应
|
| 1236 |
+
- 新增 `ui/tab_plot.py`(Tab5):两条渲染路径(⚡Interactive / 🖨️Export)
|
| 1237 |
+
- 新增 `core/table_gen.py`:6张论文表格生成(LaTeX/Markdown/CSV)
|
| 1238 |
+
- 新增 `ui/tab_tables.py`(Tab6):一键生成,批量下载
|
| 1239 |
+
- `app.py` 双语改造:英文在左,中文在右,两列并排表格
|
| 1240 |
+
|
| 1241 |
+
### v0.3 — Pseudo-bulk 聚合 + 防脏数据 + 级联删除
|
| 1242 |
+
**问题修复:**
|
| 1243 |
+
- `ui/tab_analyze.py`:`upsert_model` / `upsert_component` 推迟到 `load_all_shard_headers()` 成功后执行,防止模型名拼错产生脏数据
|
| 1244 |
+
|
| 1245 |
+
**新功能:**
|
| 1246 |
+
- `db/writer.py`:新增 `_pseudobulk_col()`,`update_model_summary()` 改用 pseudo-bulk 两步聚合,消除 GQA 伪重复计数偏差
|
| 1247 |
+
- `db/writer.py`:新增 `refresh_all_summaries()`,Tab3 Refresh 按钮触发,自动将历史数据重算为 pseudo-bulk
|
| 1248 |
+
- `db/writer.py`:新增 `delete_model(conn, model_id, admin_token)`,级联删除模型所有数据,需 WRITE_TOKEN 验证
|
| 1249 |
+
- `ui/tab_database.py`:新增 🗑️ Delete Model 区块,删除成功后自动刷新模型列表
|
| 1250 |
+
- `ui/tab_inspect.py`:全文翻译为英文,逻辑不变
|
| 1251 |
+
|
| 1252 |
+
**改动文件汇总:**
|
| 1253 |
+
|
| 1254 |
+
| 文件 | 类型 | 说明 |
|
| 1255 |
+
| -------------------- | ---- | ------------------------------------------------------- |
|
| 1256 |
+
| `db/writer.py` | 更新 | 新增 pseudo-bulk / refresh_all_summaries / delete_model |
|
| 1257 |
+
| `ui/tab_analyze.py` | 修复 | upsert_model 推迟到 shard headers 加载成功后 |
|
| 1258 |
+
| `ui/tab_database.py` | 更新 | 新增删除模型 UI |
|
| 1259 |
+
| `ui/tab_inspect.py` | 重构 | 全文英文化 |
|