Spaces:
Sleeping
Sleeping
| """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() | |