From 90dccbea0e40aab64dc80ac174196bf014c7474f Mon Sep 17 00:00:00 2001 From: admin <572701190@qq.com> Date: Tue, 30 Jun 2026 15:35:25 +0800 Subject: [PATCH] Show dataset YOLO training curves --- README.md | 2 +- backend/app/agents/evaluation_agent.py | 6 +++- backend/app/main.py | 4 +-- frontend/src/main.tsx | 11 +++++++ frontend/src/styles.css | 42 ++++++++++++++++++++++++++ 5 files changed, 61 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 9bb92a5..5884b24 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ for the `yolo.train_custom` task. The selected upload dataset also exposes direct YOLO custom train, predict, and heatmap actions; custom outputs are written under `var/custom_yolo_runs` and are scanned by the results dashboard. When a dataset is selected, the dataset panel shows its custom YOLO `best.pt`, -prediction previews, heatmap previews, and detected training curves. +prediction previews, heatmap previews, and inline training curve previews. Segmentation previews, YOLO heatmaps, and loss/metric artifacts are grouped on the results dashboard, and YOLO-style `results.csv` files are parsed into lightweight training curves. diff --git a/backend/app/agents/evaluation_agent.py b/backend/app/agents/evaluation_agent.py index 47193de..d65241d 100644 --- a/backend/app/agents/evaluation_agent.py +++ b/backend/app/agents/evaluation_agent.py @@ -44,7 +44,11 @@ def evaluate_project() -> dict: "upload_ui": "uploadDatasetFiles" in frontend_text and "labels" in frontend_text and "masks" in frontend_text, "dataset_quality_ui": "DatasetQuality" in frontend_text and "generateSelectedYoloYaml" in frontend_text, "uploaded_yolo_workflow_ui": "startSelectedYoloTrain" in frontend_text and "startSelectedYoloPredict" in frontend_text and "startSelectedYoloHeatmap" in frontend_text, - "dataset_yolo_outputs_ui": "DatasetYoloOutputs" in frontend_text and "selectedYoloOutputs" in frontend_text and "BEST.PT READY" in frontend_text, + "dataset_yolo_outputs_ui": "DatasetYoloOutputs" in frontend_text + and "selectedYoloOutputs" in frontend_text + and "BEST.PT READY" in frontend_text + and "datasetOutputCurve" in frontend_text + and "MiniCurvePlot" in frontend_text, "loss_result_ui": "loss" in frontend_text.lower() and "heatmap" in frontend_text.lower() and "CurvePanel" in frontend_text, "job_progress_ui": "JobProgressBar" in frontend_text and "progressTrack" in frontend_text, "runtime_readiness_ui": "runtimeReadiness" in frontend_text and "环境就绪" in frontend_text, diff --git a/backend/app/main.py b/backend/app/main.py index 93c9e8d..27223de 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -216,8 +216,8 @@ def api_results() -> list[dict]: @app.get("/api/results/curves") -def api_result_curves() -> list[dict]: - return scan_training_curves() +def api_result_curves(limit: int = 100) -> list[dict]: + return scan_training_curves(limit=limit) @app.get("/api/artifacts/{artifact_path:path}") diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index 17cbc50..77a23a1 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -1289,6 +1289,8 @@ function DatasetQuality({ validation }: { validation: DatasetValidation }) { function DatasetYoloOutputs({ dataset, outputs }: { dataset: UploadedDataset; outputs: DatasetYoloOutputsPayload }) { const previewItems = [...outputs.heatmaps.slice(0, 3), ...outputs.predictions.slice(0, 3)].slice(0, 6); + const primaryCurve = outputs.curves[0]; + const curveSeries = primaryCurve?.series.slice(0, 4) ?? []; return (
@@ -1315,6 +1317,15 @@ function DatasetYoloOutputs({ dataset, outputs }: { dataset: UploadedDataset; ou ))}
+ {primaryCurve && !!curveSeries.length && ( +
+
+ {primaryCurve.name} + {primaryCurve.row_count} epochs · {primaryCurve.family} +
+ +
+ )} {!!previewItems.length && (
{previewItems.map((item) => ( diff --git a/frontend/src/styles.css b/frontend/src/styles.css index 39b6440..225811f 100644 --- a/frontend/src/styles.css +++ b/frontend/src/styles.css @@ -853,6 +853,48 @@ textarea { white-space: nowrap; } +.datasetOutputCurve { + display: grid; + gap: 8px; + padding: 8px; + border-radius: 6px; + border: 1px solid var(--line); + background: #101310; +} + +.datasetOutputCurve > div { + min-width: 0; + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; +} + +.datasetOutputCurve strong, +.datasetOutputCurve span { + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.datasetOutputCurve strong { + flex: 1 1 auto; +} + +.datasetOutputCurve span { + flex: 0 1 auto; + color: var(--muted); + font-size: 11px; +} + +.datasetOutputCurve .curveSvg, +.datasetOutputCurve .curveEmpty { + min-height: 96px; + aspect-ratio: 2.6 / 1; + border-radius: 6px; +} + .datasetOutputPreview { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));