Verify YOLO heatmap generation in deep acceptance
This commit is contained in:
@@ -66,7 +66,8 @@ weight discovery. MMSeg full-model readiness is validated in
|
|||||||
|
|
||||||
For stronger runtime proof, `POST /api/acceptance/deep` runs minimal training
|
For stronger runtime proof, `POST /api/acceptance/deep` runs minimal training
|
||||||
loops for the three model families: one SegModel optimizer step, one YOLO
|
loops for the three model families: one SegModel optimizer step, one YOLO
|
||||||
segmentation epoch on a synthetic 64x64 dataset, and one MMSeg optimizer step
|
segmentation epoch on a synthetic 64x64 dataset, one YOLO GradCAM heatmap
|
||||||
|
generation pass from the trained tiny checkpoint, and one MMSeg optimizer step
|
||||||
through the full `mmcv._ext` runtime. The latest report is available from
|
through the full `mmcv._ext` runtime. The latest report is available from
|
||||||
`GET /api/acceptance/deep/latest` and is surfaced in the coverage panel.
|
`GET /api/acceptance/deep/latest` and is surfaced in the coverage panel.
|
||||||
|
|
||||||
|
|||||||
@@ -100,6 +100,29 @@ def _yolo_tiny_train_snippet(root: Path, weight: Path) -> str:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _yolo_heatmap_snippet(root: Path) -> str:
|
||||||
|
script_path = settings.source_root / "Seg_All_In_One_YoloModel" / "yolo_predict_visualize_nn.py"
|
||||||
|
return (
|
||||||
|
"from pathlib import Path; "
|
||||||
|
"import importlib.util, shutil, sys, types; "
|
||||||
|
"fake=types.ModuleType('yolo_config'); "
|
||||||
|
"fake.MODEL_CONFIGS={'YOLO11n-seg': {}}; "
|
||||||
|
"fake.TEST_IMAGE_DIR=''; fake.PREDICT_BEST_MODEL_DIR=Path('.'); fake.show_config_summary=lambda: None; "
|
||||||
|
"sys.modules['yolo_config']=fake; "
|
||||||
|
f"script=Path({str(script_path)!r}); "
|
||||||
|
"spec=importlib.util.spec_from_file_location('yolo_heatmap_mod', script); "
|
||||||
|
"mod=importlib.util.module_from_spec(spec); spec.loader.exec_module(mod); "
|
||||||
|
f"root=Path({str(root)!r}); "
|
||||||
|
"base=root/'runs'/'tiny'; "
|
||||||
|
"heatmap_root=base/'HeartMap_Visual'; "
|
||||||
|
"shutil.rmtree(heatmap_root, ignore_errors=True); "
|
||||||
|
"mod.visualize_nn_comprehensive(str(base/'weights'/'best.pt'), str(root/'images'/'val'/'sample.jpg'), base, 'best.pt', 'GradCAM', 'model.model.model[9]', 'YOLO11n-seg'); "
|
||||||
|
"outputs=sorted(heatmap_root.rglob('*.jpg')); "
|
||||||
|
"assert len(outputs) >= 2; "
|
||||||
|
"print('heatmaps', len(outputs), [str(item.relative_to(base)) for item in outputs[:4]])"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _mmseg_train_step_snippet(config_path: Path) -> str:
|
def _mmseg_train_step_snippet(config_path: Path) -> str:
|
||||||
return (
|
return (
|
||||||
"import torch; "
|
"import torch; "
|
||||||
@@ -329,23 +352,43 @@ def run_deep_acceptance() -> dict[str, Any]:
|
|||||||
yolo_weight = settings.source_root / "Seg_All_In_One_YoloModel" / "yolo11n-seg.pt"
|
yolo_weight = settings.source_root / "Seg_All_In_One_YoloModel" / "yolo11n-seg.pt"
|
||||||
mmseg_config = settings.source_root / "Seg_All_In_One_MMSeg" / "configs" / "fcn" / "fcn_r18-d8_4xb2-80k_cityscapes-512x1024.py"
|
mmseg_config = settings.source_root / "Seg_All_In_One_MMSeg" / "configs" / "fcn" / "fcn_r18-d8_4xb2-80k_cityscapes-512x1024.py"
|
||||||
|
|
||||||
|
yolo_root = fixture_root / "yolo_tiny"
|
||||||
checks = [
|
checks = [
|
||||||
{
|
{
|
||||||
"name": "segmodel_tiny_train_step",
|
"name": "segmodel_tiny_train_step",
|
||||||
"passed": False,
|
"passed": False,
|
||||||
"detail": _run_snippet(SEGMODEL_TRAIN_STEP_SNIPPET, timeout=90),
|
"detail": _run_snippet(SEGMODEL_TRAIN_STEP_SNIPPET, timeout=90),
|
||||||
},
|
},
|
||||||
{
|
]
|
||||||
|
yolo_train = {
|
||||||
"name": "yolo_tiny_segment_train_epoch",
|
"name": "yolo_tiny_segment_train_epoch",
|
||||||
"passed": False,
|
"passed": False,
|
||||||
"detail": _run_snippet(_yolo_tiny_train_snippet(fixture_root / "yolo_tiny", yolo_weight), timeout=180),
|
"detail": _run_snippet(_yolo_tiny_train_snippet(yolo_root, yolo_weight), timeout=180),
|
||||||
},
|
}
|
||||||
|
checks.append(yolo_train)
|
||||||
|
if yolo_train["detail"].get("passed"):
|
||||||
|
checks.append(
|
||||||
|
{
|
||||||
|
"name": "yolo_tiny_heatmap_generation",
|
||||||
|
"passed": False,
|
||||||
|
"detail": _run_snippet(_yolo_heatmap_snippet(yolo_root), timeout=90),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
checks.append(
|
||||||
|
{
|
||||||
|
"name": "yolo_tiny_heatmap_generation",
|
||||||
|
"passed": False,
|
||||||
|
"detail": {"passed": False, "error": "skipped because yolo_tiny_segment_train_epoch failed"},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
checks.append(
|
||||||
{
|
{
|
||||||
"name": "mmseg_tiny_train_step",
|
"name": "mmseg_tiny_train_step",
|
||||||
"passed": False,
|
"passed": False,
|
||||||
"detail": _run_conda_snippet(settings.mmseg_conda_env, _mmseg_train_step_snippet(mmseg_config), timeout=120),
|
"detail": _run_conda_snippet(settings.mmseg_conda_env, _mmseg_train_step_snippet(mmseg_config), timeout=120),
|
||||||
},
|
}
|
||||||
]
|
)
|
||||||
for check in checks:
|
for check in checks:
|
||||||
check["passed"] = bool(check["detail"].get("passed"))
|
check["passed"] = bool(check["detail"].get("passed"))
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ def evaluate_project() -> dict:
|
|||||||
|
|
||||||
frontend_text = frontend.read_text(encoding="utf-8") if frontend.exists() else ""
|
frontend_text = frontend.read_text(encoding="utf-8") if frontend.exists() else ""
|
||||||
backend_text = backend.read_text(encoding="utf-8") if backend.exists() else ""
|
backend_text = backend.read_text(encoding="utf-8") if backend.exists() else ""
|
||||||
|
acceptance_text = (settings.project_root / "backend" / "app" / "acceptance.py").read_text(encoding="utf-8")
|
||||||
readme_text = readme.read_text(encoding="utf-8") if readme.exists() else ""
|
readme_text = readme.read_text(encoding="utf-8") if readme.exists() else ""
|
||||||
|
|
||||||
expectations = {
|
expectations = {
|
||||||
@@ -44,6 +45,7 @@ def evaluate_project() -> dict:
|
|||||||
"curve_api": "/api/results/curves" in backend_text,
|
"curve_api": "/api/results/curves" in backend_text,
|
||||||
"deep_acceptance_api": "/api/acceptance/deep" in backend_text,
|
"deep_acceptance_api": "/api/acceptance/deep" in backend_text,
|
||||||
"deep_acceptance_ui": "runDeepAcceptance" in frontend_text and "深度训练" in frontend_text,
|
"deep_acceptance_ui": "runDeepAcceptance" in frontend_text and "深度训练" in frontend_text,
|
||||||
|
"deep_yolo_heatmap_validation": "yolo_tiny_heatmap_generation" in acceptance_text,
|
||||||
"coverage_api": "/api/coverage" in backend_text and coverage["task_build_passed"],
|
"coverage_api": "/api/coverage" in backend_text and coverage["task_build_passed"],
|
||||||
"visual_tools": "visual.yolo11_heatmap_v2" in catalog["task_types"] and "visual.fps" in catalog["task_types"],
|
"visual_tools": "visual.yolo11_heatmap_v2" in catalog["task_types"] and "visual.fps" in catalog["task_types"],
|
||||||
"yolo_dataset_tools": "dataset.yolo_txt_sort" in catalog["task_types"] and "dataset.yolo_resize" in catalog["task_types"],
|
"yolo_dataset_tools": "dataset.yolo_txt_sort" in catalog["task_types"] and "dataset.yolo_resize" in catalog["task_types"],
|
||||||
@@ -64,7 +66,7 @@ def evaluate_project() -> dict:
|
|||||||
if coverage["unmapped_user_scripts"]:
|
if coverage["unmapped_user_scripts"]:
|
||||||
suggestions.append(f"Map remaining user-facing scripts: {len(coverage['unmapped_user_scripts'])}")
|
suggestions.append(f"Map remaining user-facing scripts: {len(coverage['unmapped_user_scripts'])}")
|
||||||
if not suggestions:
|
if not suggestions:
|
||||||
suggestions.append("Current platform covers the requested control-plane features; next focus is real dataset/training acceptance tests.")
|
suggestions.append("Current platform covers the requested control-plane features and synthetic deep training/heatmap acceptance; next focus is a user-supplied dataset end-to-end run.")
|
||||||
|
|
||||||
score = sum(1 for item in checks if item["passed"]) / max(len(checks), 1)
|
score = sum(1 for item in checks if item["passed"]) / max(len(checks), 1)
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -32,6 +32,9 @@ def result_roots() -> list[Path]:
|
|||||||
upload_root = project / "var" / "uploads" / "datasets"
|
upload_root = project / "var" / "uploads" / "datasets"
|
||||||
if upload_root.exists():
|
if upload_root.exists():
|
||||||
roots.extend(path for path in upload_root.glob("*/results") if path.is_dir())
|
roots.extend(path for path in upload_root.glob("*/results") if path.is_dir())
|
||||||
|
acceptance_root = project / "var" / "acceptance"
|
||||||
|
if acceptance_root.exists():
|
||||||
|
roots.extend(path for path in acceptance_root.glob("deep_*/yolo_tiny/runs/tiny/HeartMap_Visual") if path.is_dir())
|
||||||
return roots
|
return roots
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user