poolcoach / scripts /cv /pick_conf.py
masterdanh's picture
deploy: snapshot for HF Space
78738de
Raw
History Blame Contribute Delete
5.73 kB
"""Chọn CONF VẬN HÀNH cho CV worker từ F1-curve any-ball trên val (bàn giao 18).
Chạy trên `poolcoach-cv-env` (cần ultralytics + GPU/CPU như lúc đo anyball):
python scripts/cv/pick_conf.py --weights "D:/Khoa luan/cv_full_20260805/best.pt" \
--artifact-dir "D:/Khoa luan/cv_full_20260805"
Cách làm: chạy ĐÚNG validator any-ball của ``eval_anyball.py`` (4 class bi
gộp thành "ball", Dot loại khỏi pred+GT, val-pipeline NMS multi_label=True)
rồi đọc các đường cong F1/P/R theo conf từ ``DetMetrics.curves_results``.
Conf vận hành = argmax của F1 đã làm mượt (cùng ``smooth`` window 0.1 mà
ultralytics dùng khi tự chọn điểm hiển thị P/R).
Neo số (self-check): AP50 any-ball của lần chạy này phải khớp
``anyball.json`` đã nghiệm thu (05/08) trong ±0.005 — lệch là DỪNG, không
ghi số (validator trôi thì mọi con số kéo theo đều vô nghĩa).
LƯU Ý HAI-CÁI-THƯỚC (bàn giao 17): các số P/R ở đây đo bằng VAL-pipeline
(rect dataloader, NMS multi_label=True). Worker deploy chạy PREDICT-pipeline
(multi_label=False) — conf chọn ở đây là điểm vận hành hợp lý, nhưng P/R
tại conf KHÔNG so ngang được với số đo trên predict-pipeline.
BRIEF "Nếu bí": conf ngoài [0.1, 0.9] là nghi có gì sai — script tự DỪNG.
Console ASCII-only (bẫy cp1252). Kết quả ghi ``opconf.json`` vào artifact dir.
"""
from __future__ import annotations
import argparse
import json
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2] # poolcoach-rl/
sys.path.insert(0, str(ROOT / "src"))
sys.path.insert(0, str(Path(__file__).resolve().parent)) # import eval_anyball
from eval_anyball import run_val # noqa: E402 — cùng validator, không chép lại
def main() -> None:
ap = argparse.ArgumentParser(description="Pick operating conf from any-ball F1 curve (val)")
ap.add_argument("--weights", type=Path, required=True)
ap.add_argument("--artifact-dir", type=Path, required=True,
help="noi ghi opconf.json + doc anyball.json lam neo self-check")
ap.add_argument("--device", default=None,
help="mac dinh: truong env.device cua anyball.json")
ap.add_argument("--tolerance", type=float, default=0.005,
help="nguong lech AP50 vs anyball.json")
args = ap.parse_args()
import numpy as np
from ultralytics.utils.metrics import smooth
anyball_json = args.artifact_dir / "anyball.json"
ref = json.loads(anyball_json.read_text(encoding="utf-8"))
device = args.device if args.device is not None else str(ref["env"]["device"])
tag = args.weights.resolve().parent.name
metrics, counters = run_val(args.weights, merged=True, device=device,
run_name=f"pickconf_{tag}")
# --- neo self-check: AP50 lan nay phai bang anyball.json da nghiem thu ---
ap50 = float(metrics.box.map50)
ref_ap50 = float(ref["anyball"]["ap50"])
d = abs(ap50 - ref_ap50)
print(f"[selfcheck] anyball AP50 lan nay {ap50:.4f} vs {ref_ap50:.4f} "
f"(anyball.json) -> lech {d:.4f}")
if d > args.tolerance:
sys.exit(f"[FAIL] lech {d:.4f} > {args.tolerance} - validator troi, "
f"KHONG ghi opconf.json.")
# --- doc curve F1/P/R theo conf tu curves_results (API cong khai) ---
curves = {ylab: (np.asarray(x), np.asarray(y))
for x, y, _xlab, ylab in metrics.curves_results
if ylab in ("F1", "Precision", "Recall")}
missing = {"F1", "Precision", "Recall"} - set(curves)
if missing:
sys.exit(f"[FAIL] curves_results thieu {missing} - phien ban "
f"ultralytics doi API?")
px, f1 = curves["F1"]
f1 = f1.mean(axis=0) # merged: 1 class, mean = chinh no
p = curves["Precision"][1].mean(axis=0)
r = curves["Recall"][1].mean(axis=0)
# cung cach ultralytics chon diem hien thi: smooth(f1, 0.1) roi argmax
i = int(smooth(f1, 0.1).argmax())
conf_op = float(px[i])
if not (0.1 <= conf_op <= 0.9):
sys.exit(f"[FAIL] conf argmax F1 = {conf_op:.3f} ngoai [0.1, 0.9] - "
f"BRIEF bao nghi co gi sai, DUNG va bao lai Cowork.")
print(f"[pickconf] conf vanh hanh = {conf_op:.3f} "
f"(F1 {f1[i]:.4f}, P {p[i]:.4f}, R {r[i]:.4f}) - val-pipeline")
print(f"[pickconf] {'conf':>6} {'F1':>7} {'P':>7} {'R':>7} (lan can)")
for j in range(max(0, i - 100), min(len(px), i + 101), 50):
mark = " <- chon" if j == i else ""
print(f"[pickconf] {px[j]:6.3f} {f1[j]:7.4f} {p[j]:7.4f} {r[j]:7.4f}{mark}")
out = {
"date": "2026-08-05",
"weights": str(args.weights),
"split": "val",
"method": ("any-ball merged validator cua eval_anyball (val-pipeline, "
"NMS multi_label=True); conf = argmax smooth(F1, 0.1); "
"P/R doc tai cung index tren p_curve/r_curve. KHONG so "
"ngang voi predict-pipeline cua worker deploy."),
"conf": round(conf_op, 4),
"f1": round(float(f1[i]), 4),
"precision": round(float(p[i]), 4),
"recall": round(float(r[i]), 4),
"selfcheck": {"ap50": round(ap50, 4), "ref_ap50": ref_ap50,
"abs_diff": round(d, 4), "tolerance": args.tolerance},
"counters": counters,
"env": {"device": device},
}
(args.artifact_dir / "opconf.json").write_text(json.dumps(out, indent=2),
encoding="utf-8")
print(f"[artifact] -> {args.artifact_dir / 'opconf.json'}")
if __name__ == "__main__":
main()