Show dataset YOLO training curves
This commit is contained in:
@@ -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
|
direct YOLO custom train, predict, and heatmap actions; custom outputs are
|
||||||
written under `var/custom_yolo_runs` and are scanned by the results dashboard.
|
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`,
|
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
|
Segmentation previews, YOLO heatmaps, and loss/metric artifacts are grouped on
|
||||||
the results dashboard, and YOLO-style `results.csv` files are parsed into
|
the results dashboard, and YOLO-style `results.csv` files are parsed into
|
||||||
lightweight training curves.
|
lightweight training curves.
|
||||||
|
|||||||
@@ -44,7 +44,11 @@ def evaluate_project() -> dict:
|
|||||||
"upload_ui": "uploadDatasetFiles" in frontend_text and "labels" in frontend_text and "masks" in frontend_text,
|
"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,
|
"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,
|
"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,
|
"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,
|
"job_progress_ui": "JobProgressBar" in frontend_text and "progressTrack" in frontend_text,
|
||||||
"runtime_readiness_ui": "runtimeReadiness" in frontend_text and "环境就绪" in frontend_text,
|
"runtime_readiness_ui": "runtimeReadiness" in frontend_text and "环境就绪" in frontend_text,
|
||||||
|
|||||||
@@ -216,8 +216,8 @@ def api_results() -> list[dict]:
|
|||||||
|
|
||||||
|
|
||||||
@app.get("/api/results/curves")
|
@app.get("/api/results/curves")
|
||||||
def api_result_curves() -> list[dict]:
|
def api_result_curves(limit: int = 100) -> list[dict]:
|
||||||
return scan_training_curves()
|
return scan_training_curves(limit=limit)
|
||||||
|
|
||||||
|
|
||||||
@app.get("/api/artifacts/{artifact_path:path}")
|
@app.get("/api/artifacts/{artifact_path:path}")
|
||||||
|
|||||||
@@ -1289,6 +1289,8 @@ function DatasetQuality({ validation }: { validation: DatasetValidation }) {
|
|||||||
|
|
||||||
function DatasetYoloOutputs({ dataset, outputs }: { dataset: UploadedDataset; outputs: DatasetYoloOutputsPayload }) {
|
function DatasetYoloOutputs({ dataset, outputs }: { dataset: UploadedDataset; outputs: DatasetYoloOutputsPayload }) {
|
||||||
const previewItems = [...outputs.heatmaps.slice(0, 3), ...outputs.predictions.slice(0, 3)].slice(0, 6);
|
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 (
|
return (
|
||||||
<div className="datasetOutputBox">
|
<div className="datasetOutputBox">
|
||||||
<div className="qualityHead">
|
<div className="qualityHead">
|
||||||
@@ -1315,6 +1317,15 @@ function DatasetYoloOutputs({ dataset, outputs }: { dataset: UploadedDataset; ou
|
|||||||
</a>
|
</a>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
{primaryCurve && !!curveSeries.length && (
|
||||||
|
<div className="datasetOutputCurve">
|
||||||
|
<div>
|
||||||
|
<strong>{primaryCurve.name}</strong>
|
||||||
|
<span>{primaryCurve.row_count} epochs · {primaryCurve.family}</span>
|
||||||
|
</div>
|
||||||
|
<MiniCurvePlot series={curveSeries} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{!!previewItems.length && (
|
{!!previewItems.length && (
|
||||||
<div className="datasetOutputPreview">
|
<div className="datasetOutputPreview">
|
||||||
{previewItems.map((item) => (
|
{previewItems.map((item) => (
|
||||||
|
|||||||
@@ -853,6 +853,48 @@ textarea {
|
|||||||
white-space: nowrap;
|
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 {
|
.datasetOutputPreview {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
|
|||||||
Reference in New Issue
Block a user