Add real YOLO train acceptance
This commit is contained in:
@@ -447,6 +447,13 @@ def latest_real_acceptance_report() -> dict[str, Any]:
|
||||
return json.loads(path.read_text(encoding="utf-8"))
|
||||
|
||||
|
||||
def latest_real_train_acceptance_report() -> dict[str, Any]:
|
||||
path = settings.project_root / "var" / "acceptance" / "real_train_latest.json"
|
||||
if not path.exists():
|
||||
return {"available": False, "path": str(path)}
|
||||
return json.loads(path.read_text(encoding="utf-8"))
|
||||
|
||||
|
||||
def run_real_dataset_acceptance(base_url: str = "http://127.0.0.1:8010") -> dict[str, Any]:
|
||||
"""Run the upload/predict/heatmap path against existing non-synthetic Seg data."""
|
||||
acceptance_root = settings.project_root / "var" / "acceptance"
|
||||
@@ -620,6 +627,178 @@ def run_real_dataset_acceptance(base_url: str = "http://127.0.0.1:8010") -> dict
|
||||
return report
|
||||
|
||||
|
||||
def run_real_train_acceptance(base_url: str = "http://127.0.0.1:8010") -> dict[str, Any]:
|
||||
"""Run a short YOLO train/predict/heatmap loop using real workspace samples."""
|
||||
acceptance_root = settings.project_root / "var" / "acceptance"
|
||||
run_id = uuid.uuid4().hex[:8]
|
||||
fixture_root = acceptance_root / f"real_train_{run_id}"
|
||||
fixture_root.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
samples = find_real_workspace_samples()
|
||||
checks: list[dict[str, Any]] = [
|
||||
{"name": "real_train_workspace_samples_discovered", "passed": samples["passed"], "detail": samples}
|
||||
]
|
||||
if not samples["passed"]:
|
||||
report = {
|
||||
"available": True,
|
||||
"run_id": run_id,
|
||||
"base_url": base_url,
|
||||
"fixture_root": str(fixture_root),
|
||||
"passed": False,
|
||||
"checks": checks,
|
||||
"created_at": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
|
||||
}
|
||||
(acceptance_root / "real_train_latest.json").write_text(json.dumps(report, ensure_ascii=False, indent=2), encoding="utf-8")
|
||||
return report
|
||||
|
||||
dataset_name = f"real_train_acceptance_{run_id}"
|
||||
created_dataset = _request_json("POST", f"{base_url}/api/datasets", {"name": dataset_name, "description": "real workspace short train acceptance"}, timeout=10)
|
||||
checks.append({"name": "create_real_train_upload_dataset", "passed": created_dataset.get("passed", False), "detail": created_dataset})
|
||||
|
||||
yolo_image = Path(samples["yolo_pair"]["image"])
|
||||
yolo_label = Path(samples["yolo_pair"]["label"])
|
||||
uploads = {
|
||||
"real_train_yolo_image_upload": _post_file(f"{base_url}/api/datasets/{dataset_name}/upload/images", yolo_image, timeout=30),
|
||||
"real_train_yolo_label_upload": _post_file(f"{base_url}/api/datasets/{dataset_name}/upload/labels", yolo_label, timeout=30),
|
||||
}
|
||||
for name, detail in uploads.items():
|
||||
checks.append({"name": name, "passed": detail.get("passed", False), "detail": detail})
|
||||
|
||||
validation = _request_json("GET", f"{base_url}/api/datasets/{dataset_name}/validate", timeout=20)
|
||||
validation_json = validation.get("json") if validation.get("passed") else {}
|
||||
checks.append(
|
||||
{
|
||||
"name": "real_train_dataset_validate_yolo",
|
||||
"passed": validation.get("passed", False) and validation_json.get("ready", {}).get("yolo"),
|
||||
"detail": validation,
|
||||
}
|
||||
)
|
||||
|
||||
class_count = max(validation_json.get("classes") or [0]) + 1
|
||||
class_names = ["object"] + [f"class_{index}" for index in range(1, class_count)]
|
||||
yolo_yaml = _request_json("POST", f"{base_url}/api/datasets/{dataset_name}/yolo-yaml", {"class_names": class_names}, timeout=20)
|
||||
yolo_yaml_json = yolo_yaml.get("json") if yolo_yaml.get("passed") else {}
|
||||
checks.append({"name": "real_train_dataset_yolo_yaml", "passed": yolo_yaml.get("passed", False), "detail": yolo_yaml})
|
||||
|
||||
train_name = f"{dataset_name}_train"
|
||||
train = _create_job_and_wait(
|
||||
base_url,
|
||||
"yolo.train_custom",
|
||||
{
|
||||
"data": yolo_yaml_json.get("relative_path", f"var/uploads/datasets/{dataset_name}/dataset.yaml"),
|
||||
"model": str(settings.source_root / "Seg_All_In_One_YoloModel" / "yolo11n-seg.pt"),
|
||||
"project": "var/custom_yolo_runs",
|
||||
"name": train_name,
|
||||
"epochs": 1,
|
||||
"imgsz": 96,
|
||||
"batch": 1,
|
||||
"workers": 0,
|
||||
"device": "cpu",
|
||||
"exist_ok": True,
|
||||
},
|
||||
timeout=240,
|
||||
)
|
||||
train_root = settings.project_root / "var" / "custom_yolo_runs" / train_name
|
||||
best_weight = train_root / "weights" / "best.pt"
|
||||
last_weight = train_root / "weights" / "last.pt"
|
||||
results_csv = train_root / "results.csv"
|
||||
checks.append(
|
||||
{
|
||||
"name": "real_train_yolo_one_epoch_job_runner",
|
||||
"passed": train.get("passed", False) and best_weight.exists() and results_csv.exists() and results_csv.stat().st_size > 0,
|
||||
"detail": {
|
||||
**train,
|
||||
"best_weight": _relative_to_project(best_weight) if best_weight.exists() else None,
|
||||
"last_weight": _relative_to_project(last_weight) if last_weight.exists() else None,
|
||||
"results_csv": _relative_to_project(results_csv) if results_csv.exists() else None,
|
||||
"results_csv_size": results_csv.stat().st_size if results_csv.exists() else 0,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
uploaded_image_json = uploads["real_train_yolo_image_upload"].get("json", {})
|
||||
uploaded_image = uploaded_image_json.get("saved", [{}])[0].get("relative_path")
|
||||
predict_name = f"{dataset_name}_predict_trained"
|
||||
if best_weight.exists() and uploaded_image:
|
||||
predict = _create_job_and_wait(
|
||||
base_url,
|
||||
"yolo.predict_custom",
|
||||
{
|
||||
"weights": str(best_weight),
|
||||
"source": uploaded_image,
|
||||
"project": "var/custom_yolo_runs",
|
||||
"name": predict_name,
|
||||
"imgsz": 96,
|
||||
"conf": 0.01,
|
||||
"device": "cpu",
|
||||
"exist_ok": True,
|
||||
},
|
||||
timeout=120,
|
||||
)
|
||||
else:
|
||||
predict = {"passed": False, "error": "skipped because training did not produce best.pt or upload path"}
|
||||
predict_root = settings.project_root / "var" / "custom_yolo_runs" / predict_name
|
||||
predict_outputs = _result_files(predict_root, {".png", ".jpg", ".jpeg"})
|
||||
checks.append(
|
||||
{
|
||||
"name": "real_train_trained_weight_predict_job_runner",
|
||||
"passed": predict.get("passed", False) and bool(predict_outputs),
|
||||
"detail": {**predict, "output_count": len(predict_outputs), "outputs": [_relative_to_project(path) for path in predict_outputs[:8]]},
|
||||
}
|
||||
)
|
||||
|
||||
heatmap_name = f"{dataset_name}_heatmap_trained"
|
||||
if best_weight.exists() and uploaded_image:
|
||||
heatmap = _create_job_and_wait(
|
||||
base_url,
|
||||
"yolo.heatmap_custom",
|
||||
{
|
||||
"weights": str(best_weight),
|
||||
"source": uploaded_image,
|
||||
"project": "var/custom_yolo_runs",
|
||||
"name": heatmap_name,
|
||||
"model_key": "YOLO11n-seg",
|
||||
"pt_name": "best.pt",
|
||||
"cam_method": "GradCAM",
|
||||
"target_layers": "model.model.model[9]",
|
||||
"limit": 1,
|
||||
},
|
||||
timeout=120,
|
||||
)
|
||||
else:
|
||||
heatmap = {"passed": False, "error": "skipped because training did not produce best.pt or upload path"}
|
||||
heatmap_root = settings.project_root / "var" / "custom_yolo_runs" / heatmap_name / "HeartMap_Visual"
|
||||
heatmap_outputs = _result_files(heatmap_root, {".jpg", ".jpeg", ".png"})
|
||||
checks.append(
|
||||
{
|
||||
"name": "real_train_trained_weight_heatmap_job_runner",
|
||||
"passed": heatmap.get("passed", False) and len(heatmap_outputs) >= 2,
|
||||
"detail": {**heatmap, "output_count": len(heatmap_outputs), "outputs": [_relative_to_project(path) for path in heatmap_outputs[:8]]},
|
||||
}
|
||||
)
|
||||
|
||||
report = {
|
||||
"available": True,
|
||||
"run_id": run_id,
|
||||
"base_url": base_url,
|
||||
"fixture_root": str(fixture_root),
|
||||
"dataset_name": dataset_name,
|
||||
"samples": samples,
|
||||
"artifacts": {
|
||||
"train_root": _relative_to_project(train_root),
|
||||
"best_weight": _relative_to_project(best_weight) if best_weight.exists() else None,
|
||||
"results_csv": _relative_to_project(results_csv) if results_csv.exists() else None,
|
||||
"predict_outputs": [_relative_to_project(path) for path in predict_outputs[:8]],
|
||||
"heatmap_outputs": [_relative_to_project(path) for path in heatmap_outputs[:8]],
|
||||
},
|
||||
"passed": all(item["passed"] for item in checks),
|
||||
"checks": checks,
|
||||
"created_at": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
|
||||
}
|
||||
(acceptance_root / "real_train_latest.json").write_text(json.dumps(report, ensure_ascii=False, indent=2), encoding="utf-8")
|
||||
return report
|
||||
|
||||
|
||||
def run_deep_acceptance() -> dict[str, Any]:
|
||||
"""Run minimal training loops for each model family without full datasets."""
|
||||
acceptance_root = settings.project_root / "var" / "acceptance"
|
||||
|
||||
Reference in New Issue
Block a user