Files
modeling_zhaocui/deep_learning/Q1/q1/summarize_alignment_debug.py
T

195 lines
8.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Summarize the saved D0-D3 alignment-diagnostic metrics without retraining."""
from __future__ import annotations
import argparse
import csv
from collections import defaultdict
from pathlib import Path
import shutil
from statistics import mean
from typing import Any
METRICS = (
"mvr",
"normalized_entropy",
"c_row",
"trajectory_span",
"mean_absolute_time_center_error",
"gaussian_target_kl",
)
def merge_trial_metrics(run_dir: Path, destination: Path) -> None:
"""Rebuild the combined file with a variant label for PE controls."""
rows: list[dict[str, str]] = []
fields: list[str] | None = None
for experiment in ("D0", "D1", "D2", "D3"):
for path in sorted((run_dir / experiment).glob("*_metrics.csv")):
variant = path.name.removesuffix("_metrics.csv")
with path.open(newline="", encoding="utf-8-sig") as handle:
reader = csv.DictReader(handle)
if fields is None:
fields = ["experiment", "method", "variant"] + [
name for name in (reader.fieldnames or [])
if name not in {"experiment", "method", "variant"}
]
for row in reader:
row["variant"] = variant
rows.append(row)
if not fields:
raise FileNotFoundError(f"no per-trial metric CSV files found under {run_dir}")
destination.parent.mkdir(parents=True, exist_ok=True)
with destination.open("w", newline="", encoding="utf-8-sig") as handle:
writer = csv.DictWriter(handle, fieldnames=fields)
writer.writeheader()
writer.writerows(rows)
def summarize(source: Path, destination: Path) -> list[dict[str, Any]]:
groups: dict[tuple[str, str, str], list[dict[str, str]]] = defaultdict(list)
with source.open(newline="", encoding="utf-8-sig") as handle:
for row in csv.DictReader(handle):
if row["experiment"] in {"D2", "D3"}:
groups[(row["experiment"], row["method"], row["modality"])].append(row)
summaries: list[dict[str, Any]] = []
for (experiment, method, modality), rows in sorted(groups.items()):
summary: dict[str, Any] = {
"experiment": experiment,
"method": method,
"modality": modality,
"sample_count": len(rows),
}
for metric in METRICS:
values = [float(row[metric]) for row in rows if row.get(metric, "") != ""]
summary[f"mean_{metric}"] = mean(values) if values else ""
summary[f"n_{metric}"] = len(values)
summaries.append(summary)
destination.parent.mkdir(parents=True, exist_ok=True)
with destination.open("w", newline="", encoding="utf-8-sig") as handle:
writer = csv.DictWriter(handle, fieldnames=list(summaries[0]))
writer.writeheader()
writer.writerows(summaries)
return summaries
def build_report_bundle(run_dir: Path) -> Path:
"""Copy compact diagnostic evidence, leaving large checkpoints local."""
bundle = run_dir / "report_bundle"
bundle.mkdir(parents=True, exist_ok=True)
for name in (
"debug_summary.csv",
"metric_summary.csv",
"per_sample_metrics.csv",
"run_manifest.json",
"experiment.log",
):
source = run_dir / name
if source.exists():
shutil.copy2(source, bundle / name)
for experiment in ("D0", "D1", "D2", "D3"):
source_dir = run_dir / experiment
target_dir = bundle / experiment
target_dir.mkdir(exist_ok=True)
for pattern in ("*_history.csv", "*_metrics.csv", "*_heatmap.png", "*_trajectory.png"):
for source in source_dir.glob(pattern):
shutil.copy2(source, target_dir / source.name)
synthetic_source = run_dir / "synthetic"
synthetic_target = bundle / "synthetic"
synthetic_target.mkdir(exist_ok=True)
for name in ("metrics.csv", "training_history.csv", "run_manifest.json"):
source = synthetic_source / name
if source.exists():
shutil.copy2(source, synthetic_target / name)
for source in synthetic_source.rglob("*.png"):
target = synthetic_target / source.relative_to(synthetic_source)
target.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(source, target)
source_time_dir = run_dir / "source_time"
source_time_target = bundle / "source_time"
source_time_target.mkdir(exist_ok=True)
for name in ("summary.csv", "per_sample_metrics.csv", "run_manifest.json"):
source = source_time_dir / name
if source.exists():
shutil.copy2(source, source_time_target / name)
for variant_dir in source_time_dir.glob("M*"):
target_dir = source_time_target / variant_dir.name
target_dir.mkdir(exist_ok=True)
for pattern in ("history.csv", "metrics.csv", "*_heatmap.png", "*_trajectory.png"):
for source in variant_dir.glob(pattern):
shutil.copy2(source, target_dir / source.name)
heldout_dir = run_dir / "heldout"
heldout_target = bundle / "heldout"
heldout_target.mkdir(exist_ok=True)
for name in (
"heldout_summary.csv",
"per_sample_metrics.csv",
"training_summary.csv",
"training_history.csv",
"run_manifest.json",
):
source = heldout_dir / name
if source.exists():
shutil.copy2(source, heldout_target / name)
for variant_dir in heldout_dir.glob("M*"):
target_dir = heldout_target / variant_dir.name
target_dir.mkdir(exist_ok=True)
for pattern in ("history.csv", "heldout_metrics.csv", "*_heatmap.png", "*_trajectory.png"):
for source in variant_dir.glob(pattern):
shutil.copy2(source, target_dir / source.name)
readme = """# Q1 M3/M4 可学习性诊断结果包
本包包含 D0–D3 诊断的汇总表、运行清单、日志、各试验的损失历史、逐样本指标和代表性图像。PyTorch 检查点与逐样本 `.npz` 注意力矩阵留在上级 `outputs/alignment_debug/`,因此结果包较轻。
- D0/D1:单样本过拟合诊断。
- D1-S:输入仅为合成时间坐标;M3/M4 都能把 Gaussian 目标拟合至约 1e-4 KL。
- D2/D3:100 条样本上的同集训练/评价诊断,单个随机种子,不用于声称泛化。
- D4:真实单样本加入显式时间 key/query 特征后,Audio 注意力形成局部时间带。
- D5:按 `video_id` 留出 20 条样本;M4 的 Audio/Vision 时间带能迁移,M3 有改善但仍未充分贴近目标。
- Gaussian 时间目标是根据源时间戳生成的弱先验,不是人工对齐真值。
- M3 的 Gaussian KL 取 Audio/Vision 平均,M4 取 Text/Audio/Vision 平均;KL 数值仅用于各自的优化诊断,不可作为方法排名。
- D1 真实特征上 Text/Vision 能拟合,Audio 仍失败;合成对照成功,提示真实源特征的显式时间身份值得优先验证。
- 全数据 Q/K 梯度仍非零,但学习到的注意力没有稳定形成局部时间带;加入重构和对比目标后 Audio 塌缩更明显。
- 环境:Fedora WSL,`uv`,NVIDIA GeForce RTX 5070 Ti,Python 3.14.7,PyTorch 2.14.0+cu130。
详细解释见项目根目录的 `RESULTS.md`。
"""
(bundle / "README.md").write_text(readme, encoding="utf-8")
return bundle
def main() -> None:
project = Path(__file__).resolve().parents[1]
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"--input",
type=Path,
default=project / "outputs/alignment_debug/per_sample_metrics.csv",
)
parser.add_argument(
"--output",
type=Path,
default=project / "outputs/alignment_debug/metric_summary.csv",
)
args = parser.parse_args()
merge_trial_metrics(args.input.parent, args.input)
for row in summarize(args.input, args.output):
values = " ".join(
f"{name}={row[f'mean_{name}']:.4f}"
for name in METRICS
if row[f"mean_{name}"] != ""
)
print(
f"{row['experiment']} {row['method']} {row['modality']} "
f"n={row['sample_count']} {values}"
)
print(f"Wrote {args.output}")
print(f"Built report bundle at {build_report_bundle(args.input.parent)}")
if __name__ == "__main__":
main()