From 43ed767b4faf139de164d1dec4254f1ba39db388 Mon Sep 17 00:00:00 2001 From: admin <572701190@qq.com> Date: Tue, 30 Jun 2026 13:52:00 +0800 Subject: [PATCH] Verify YOLO heatmap generation in deep acceptance --- README.md | 3 +- backend/app/acceptance.py | 57 ++++++++++++++++++++++---- backend/app/agents/evaluation_agent.py | 4 +- backend/app/modules/results/service.py | 3 ++ 4 files changed, 58 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 5430932..579cb71 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,8 @@ weight discovery. MMSeg full-model readiness is validated in For stronger runtime proof, `POST /api/acceptance/deep` runs minimal training 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 `GET /api/acceptance/deep/latest` and is surfaced in the coverage panel. diff --git a/backend/app/acceptance.py b/backend/app/acceptance.py index 579308e..dd212de 100644 --- a/backend/app/acceptance.py +++ b/backend/app/acceptance.py @@ -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: return ( "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" 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 = [ { "name": "segmodel_tiny_train_step", "passed": False, "detail": _run_snippet(SEGMODEL_TRAIN_STEP_SNIPPET, timeout=90), }, - { - "name": "yolo_tiny_segment_train_epoch", - "passed": False, - "detail": _run_snippet(_yolo_tiny_train_snippet(fixture_root / "yolo_tiny", yolo_weight), timeout=180), - }, + ] + yolo_train = { + "name": "yolo_tiny_segment_train_epoch", + "passed": False, + "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", "passed": False, "detail": _run_conda_snippet(settings.mmseg_conda_env, _mmseg_train_step_snippet(mmseg_config), timeout=120), - }, - ] + } + ) for check in checks: check["passed"] = bool(check["detail"].get("passed")) diff --git a/backend/app/agents/evaluation_agent.py b/backend/app/agents/evaluation_agent.py index a60aaa6..1721cd4 100644 --- a/backend/app/agents/evaluation_agent.py +++ b/backend/app/agents/evaluation_agent.py @@ -34,6 +34,7 @@ def evaluate_project() -> dict: frontend_text = frontend.read_text(encoding="utf-8") if frontend.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 "" expectations = { @@ -44,6 +45,7 @@ def evaluate_project() -> dict: "curve_api": "/api/results/curves" in backend_text, "deep_acceptance_api": "/api/acceptance/deep" in backend_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"], "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"], @@ -64,7 +66,7 @@ def evaluate_project() -> dict: if coverage["unmapped_user_scripts"]: suggestions.append(f"Map remaining user-facing scripts: {len(coverage['unmapped_user_scripts'])}") 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) return { diff --git a/backend/app/modules/results/service.py b/backend/app/modules/results/service.py index 12fb070..03593f2 100644 --- a/backend/app/modules/results/service.py +++ b/backend/app/modules/results/service.py @@ -32,6 +32,9 @@ def result_roots() -> list[Path]: upload_root = project / "var" / "uploads" / "datasets" if upload_root.exists(): 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