initial commit

This commit is contained in:
pikaliov
2026-07-07 16:22:49 +03:00
commit 3e95f4f618
21 changed files with 3302 additions and 0 deletions

286
collect_results.py Normal file
View File

@@ -0,0 +1,286 @@
"""Сборка результатов экспериментов: таблица + CSV + графики.
Использование:
python collect_results.py # v1 + v2
python collect_results.py v1 # только v1
python collect_results.py v2 # только v2
"""
from __future__ import annotations
import csv
import json
import sys
from pathlib import Path
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
SCRIPT_DIR = Path(__file__).resolve().parent
OUTPUTS_DIR = SCRIPT_DIR / "outputs"
RESULTS_DIR = SCRIPT_DIR / "results"
# ---------------------------------------------------------------------------
# Загрузка
# ---------------------------------------------------------------------------
def load_experiment(exp_dir: Path) -> dict | None:
history_path = exp_dir / "history.json"
config_path = exp_dir / "config.json"
if not history_path.exists():
return None
with open(history_path) as f:
history = json.load(f)
config = {}
if config_path.exists():
with open(config_path) as f:
config = json.load(f)
if not history:
return None
best = max(history, key=lambda r: r.get("eval_recall@1", 0))
latest = history[-1]
return {
"dir": exp_dir.name,
"text_levels": " + ".join(config.get("text_levels", ["?"])),
"epochs_done": latest["epoch"],
"epochs_total": config.get("epochs", "?"),
"best_epoch": best["epoch"],
"best_R@1": best.get("eval_recall@1", 0),
"best_R@5": best.get("eval_recall@5", 0),
"best_R@10": best.get("eval_recall@10", 0),
"best_AP": best.get("eval_AP", 0),
"latest_loss": latest.get("train_loss", 0),
"latest_R@1": latest.get("eval_recall@1", 0),
"avg_epoch_time": sum(r.get("elapsed_s", 0) for r in history) / len(history),
"_history": history,
}
def collect_version(version: str) -> list[dict]:
"""Собрать результаты одной версии (v1 или v2)."""
version_dir = OUTPUTS_DIR / version
if not version_dir.exists():
print(f"⚠️ Папка не найдена: {version_dir}")
return []
results = []
for exp_dir in sorted(version_dir.iterdir()):
if exp_dir.is_dir() and (exp_dir / "history.json").exists():
data = load_experiment(exp_dir)
if data:
data["version"] = version
results.append(data)
return results
# ---------------------------------------------------------------------------
# Таблица в консоль
# ---------------------------------------------------------------------------
def print_table(results: list[dict], version: str) -> None:
if not results:
print(f" {version}: нет данных")
return
results.sort(key=lambda r: -r["best_R@1"])
header = (
f"{'Levels':<24} {'Prog':<8} "
f"{'BestEp':>6} {'R@1':>7} {'R@5':>7} {'R@10':>7} "
f"{'AP':>7} {'Loss':>8} {'Time':>6}"
)
sep = "" * len(header)
print(sep)
print(f" {version.upper()} Results")
print(sep)
print(header)
print(sep)
for r in results:
prog = f"{r['epochs_done']}/{r['epochs_total']}"
print(
f"{r['text_levels']:<24} {prog:<8} "
f"{r['best_epoch']:>6} {r['best_R@1']:>7.4f} {r['best_R@5']:>7.4f} "
f"{r['best_R@10']:>7.4f} {r['best_AP']:>7.4f} "
f"{r['latest_loss']:>8.4f} {r['avg_epoch_time']:>5.0f}s"
)
print(sep)
print(f" {len(results)} experiments")
print()
# ---------------------------------------------------------------------------
# CSV
# ---------------------------------------------------------------------------
def save_csv(results: list[dict], version: str) -> None:
fields = [
"version", "text_levels", "epochs_done", "epochs_total",
"best_epoch", "best_R@1", "best_R@5", "best_R@10", "best_AP",
"latest_loss", "latest_R@1", "avg_epoch_time", "dir",
]
path = RESULTS_DIR / f"results_{version}.csv"
with open(path, "w", newline="") as f:
writer = csv.DictWriter(f, fieldnames=fields, extrasaction="ignore")
writer.writeheader()
for r in results:
writer.writerow(r)
print(f" CSV: {path}")
# ---------------------------------------------------------------------------
# Графики
# ---------------------------------------------------------------------------
def save_plots(results: list[dict], version: str) -> None:
if not results:
return
colors = plt.cm.tab10(np.linspace(0, 1, max(len(results), 1)))
# --- 1. Loss ---
fig, ax = plt.subplots(figsize=(8, 5))
for r, c in zip(results, colors):
h = r["_history"]
ax.plot([e["epoch"] for e in h],
[e.get("train_loss", 0) for e in h],
label=r["text_levels"], color=c, linewidth=1.5)
ax.set_xlabel("Epoch")
ax.set_ylabel("Train Loss")
ax.set_title(f"{version.upper()} — Training Loss")
ax.legend(fontsize=8)
ax.grid(True, alpha=0.3)
plt.tight_layout()
path = RESULTS_DIR / f"loss_{version}.png"
plt.savefig(path, dpi=150)
plt.close()
print(f" PNG: {path}")
# --- 2. Recall@1 ---
fig, ax = plt.subplots(figsize=(8, 5))
for r, c in zip(results, colors):
h = r["_history"]
eps = [e["epoch"] for e in h if e.get("eval_recall@1") is not None]
r1s = [e["eval_recall@1"] for e in h if e.get("eval_recall@1") is not None]
if eps:
ax.plot(eps, r1s, label=r["text_levels"], color=c,
linewidth=1.5, marker=".", markersize=3)
ax.set_xlabel("Epoch")
ax.set_ylabel("Recall@1")
ax.set_title(f"{version.upper()} — Recall@1")
ax.legend(fontsize=8)
ax.grid(True, alpha=0.3)
plt.tight_layout()
path = RESULTS_DIR / f"recall1_{version}.png"
plt.savefig(path, dpi=150)
plt.close()
print(f" PNG: {path}")
# --- 3. Recall@5 ---
fig, ax = plt.subplots(figsize=(8, 5))
for r, c in zip(results, colors):
h = r["_history"]
eps = [e["epoch"] for e in h if e.get("eval_recall@5") is not None]
r5s = [e["eval_recall@5"] for e in h if e.get("eval_recall@5") is not None]
if eps:
ax.plot(eps, r5s, label=r["text_levels"], color=c,
linewidth=1.5, marker=".", markersize=3)
ax.set_xlabel("Epoch")
ax.set_ylabel("Recall@5")
ax.set_title(f"{version.upper()} — Recall@5")
ax.legend(fontsize=8)
ax.grid(True, alpha=0.3)
plt.tight_layout()
path = RESULTS_DIR / f"recall5_{version}.png"
plt.savefig(path, dpi=150)
plt.close()
print(f" PNG: {path}")
# --- 4. Recall@10 ---
fig, ax = plt.subplots(figsize=(8, 5))
for r, c in zip(results, colors):
h = r["_history"]
eps = [e["epoch"] for e in h if e.get("eval_recall@10") is not None]
r10s = [e["eval_recall@10"] for e in h if e.get("eval_recall@10") is not None]
if eps:
ax.plot(eps, r10s, label=r["text_levels"], color=c,
linewidth=1.5, marker=".", markersize=3)
ax.set_xlabel("Epoch")
ax.set_ylabel("Recall@10")
ax.set_title(f"{version.upper()} — Recall@10")
ax.legend(fontsize=8)
ax.grid(True, alpha=0.3)
plt.tight_layout()
path = RESULTS_DIR / f"recall10_{version}.png"
plt.savefig(path, dpi=150)
plt.close()
print(f" PNG: {path}")
# --- 3. Bar chart лучших ---
fig, ax = plt.subplots(figsize=(8, 5))
labels = [r["text_levels"] for r in results]
r1 = [r["best_R@1"] for r in results]
r5 = [r["best_R@5"] for r in results]
r10 = [r["best_R@10"] for r in results]
x = np.arange(len(labels))
w = 0.25
ax.bar(x - w, r1, w, label="R@1", color="#4C78A8")
ax.bar(x, r5, w, label="R@5", color="#54A24B")
ax.bar(x + w, r10, w, label="R@10", color="#E45756")
ax.set_xticks(x)
ax.set_xticklabels(labels, fontsize=8, rotation=20, ha="right")
ax.set_ylabel("Score")
ax.set_title(f"{version.upper()} — Best Recall")
ax.legend()
ax.grid(True, alpha=0.3, axis="y")
for i, v in enumerate(r1):
ax.text(i - w, v + 0.005, f"{v:.3f}", ha="center", fontsize=7)
plt.tight_layout()
path = RESULTS_DIR / f"best_recall_{version}.png"
plt.savefig(path, dpi=150)
plt.close()
print(f" PNG: {path}")
# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------
def main():
# Парсинг: python collect_results.py [v1|v2]
if len(sys.argv) > 1 and sys.argv[1] in ("v1", "v2"):
versions = [sys.argv[1]]
else:
versions = ["v1", "v2"]
if not OUTPUTS_DIR.exists():
print(f"❌ Папка outputs не найдена: {OUTPUTS_DIR}")
return
RESULTS_DIR.mkdir(exist_ok=True)
for version in versions:
results = collect_version(version)
# Таблица в консоль
print_table(results, version)
if results:
# CSV и графики в results/
save_csv(results, version)
save_plots(results, version)
print(f"\n📁 Все результаты: {RESULTS_DIR}")
if __name__ == "__main__":
main()