From 2089efb1cb6dccf6ea7256b62e12e741336bf10d Mon Sep 17 00:00:00 2001 From: ClovertaTheTrilobita Date: Fri, 17 Oct 2025 12:41:34 +0000 Subject: [PATCH] =?UTF-8?q?feat(frontend):=20=E5=89=8D=E7=AB=AF=E6=A0=B7?= =?UTF-8?q?=E5=BC=8F=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/flaskApp.py | 47 ++++-- frontend/run.html | 1 + frontend/train.html | 257 ++++++++++++++++++++++++-------- frontend/train_result.html | 295 +++++++++++++++++++++++++------------ 4 files changed, 437 insertions(+), 163 deletions(-) diff --git a/backend/flaskApp.py b/backend/flaskApp.py index e3c14b9..5441dbc 100644 --- a/backend/flaskApp.py +++ b/backend/flaskApp.py @@ -140,22 +140,47 @@ def run_upload(): @app.post("/train_upload") def train_upload(): + + def _to_float(x, default): + try: + return float(x) + except (TypeError, ValueError): + return default + + def _to_int(x, default): + try: + return int(x) + except (TypeError, ValueError): + return default + ts = datetime.datetime.now().strftime("%Y-%m-%d-%H-%M-%S") + f"-{int(time.time()*1000)%1000:03d}" model_name = request.args.get("model_name") or f"custom_model-{ts}" image_filter = request.args.get("image_filter") or "_img" mask_filter = request.args.get("mask_filter") or "_masks" base_model = request.args.get("base_model") or "cpsam" - batch_size = request.args.get("batch_size") or 8 - learning_rate = request.args.get("learning_rate") or 5e-5 - n_epochs = request.args.get("n_epochs") or 100 - weight_decay = request.args.get("weight_decay") or 0.1 - normalize = request.args.get("normalize") or True - compute_flows = request.args.get("compute_flows") or False - min_train_masks = request.args.get(" min_train_masks") or 5 - nimg_per_epoch = request.args.get("nimg_per_epoch") or None - rescale = request.args.get("rescale") or False - scale_range = request.args.get("scale_range") or None - channel_axis = request.args.get("channel_axis") or None + batch_size = _to_int(request.args.get("batch_size"), 8) + learning_rate = _to_float(request.args.get("learning_rate"), 5e-5) + n_epochs = _to_int(request.args.get("n_epochs"), 100) + weight_decay = _to_float(request.args.get("weight_decay"), 0.1) + normalize = request.args.get( + "normalize", + default=True, + type=lambda v: str(v).strip().lower() in ("1","true","t","yes","y","on") + ) + compute_flows = request.args.get( + "compute_flows", + default=True, + type=lambda v: str(v).strip().lower() in ("1","true","t","yes","y","on") + ) + min_train_masks = _to_int(request.args.get(" min_train_masks"), 5) + nimg_per_epoch = _to_int(request.args.get("nimg_per_epoch"), None) + rescale = request.args.get( + "rescale", + default=False, + type=lambda v: str(v).strip().lower() in ("1","true","t","yes","y","on") + ) + scale_range = _to_float(request.args.get("scale_range"), None) + channel_axis = _to_int(request.args.get("channel_axis"), None) train_files = request.files.getlist("train_files") test_files = request.files.getlist("test_files") diff --git a/frontend/run.html b/frontend/run.html index b6d54e6..9560c0a 100644 --- a/frontend/run.html +++ b/frontend/run.html @@ -7,6 +7,7 @@ Bootstrap demo + diff --git a/frontend/train.html b/frontend/train.html index 514bb93..f1b386b 100644 --- a/frontend/train.html +++ b/frontend/train.html @@ -12,54 +12,82 @@ @@ -68,7 +96,9 @@
-

训练配置

+

+ 训练配置 +

选择训练/测试数据并设置模型参数
@@ -76,61 +106,150 @@
- +
+
可多选;支持拖拽到输入框。
+
- +
+
可选;用于评估训练效果。

- +
-
-
-
- model name - -
+ +
+
+ 基础设置
-
-
- image filter - -
-
- -
-
- masks filter - -
-
- -
-
+ + +
+
+ 训练超参数 +
+ +
+
+
+ learning rate + +
+
+ +
+
+ n epochs + +
+
+ +
+
+ weight decay + +
+
+ +
+
+ normalize + +
+
+ +
+
+ compute flows + + +
+
与光流/流场相关,通常仅在特定任务启用。
+
+ +
+
+ min train masks + +
+
+ +
+
+ nimg_per_epoch + +
+
+ +
+
+ channel axis + +
+
- +
@@ -139,7 +258,8 @@
• 名称用于区分训练产物(日志/权重)。
• filter 用于匹配文件名,如 *_img.png*_masks.png
- • 选择合适的预训练模型可加速收敛。 + • 选择合适的预训练模型可加速收敛。
+ • 建议仅微调必要超参,保持可复现性。
@@ -153,12 +273,11 @@
进度 - +
-
@@ -239,13 +358,29 @@ const model_name = (document.getElementById('model_name')?.value || '').trim(); const image_filter = (document.getElementById('image_filter')?.value || '').trim(); const masks_filter = (document.getElementById('masks_filter')?.value || '').trim(); + const learning_rate = (document.getElementById('learning_rate')?.value || '').trim(); + const n_epochs = (document.getElementById('n_epochs')?.value || '').trim(); + const weight_decay = (document.getElementById('weight_decay')?.value || '').trim(); + const normalize = document.getElementById('normalize')?.value; + const compute_flows = document.getElementById('compute_flows')?.value; + const min_train_masks = (document.getElementById('min_train_masks')?.value || '').trim(); + const nimg_per_epoch = (document.getElementById('nimg_per_epoch')?.value || '').trim(); + const channel_axis = (document.getElementById('weight_decay')?.value || '').trim(); // 用 URLSearchParams 组装查询串 const qs = new URLSearchParams({ model: model, model_name: model_name, image_filter: image_filter, - masks_filter: masks_filter + masks_filter: masks_filter, + learning_rate: learning_rate, + n_epochs: n_epochs, + weight_decay: weight_decay, + normalize: normalize, + compute_flows: compute_flows, + min_train_masks: min_train_masks, + nimg_per_epoch: nimg_per_epoch, + channel_axis: channel_axis }); return `${API_UPLOAD}?${qs.toString()}`; diff --git a/frontend/train_result.html b/frontend/train_result.html index 409952a..de088ef 100644 --- a/frontend/train_result.html +++ b/frontend/train_result.html @@ -1,96 +1,209 @@ - - -

训练结果预览

- - + + - - - - \ No newline at end of file + @media (prefers-color-scheme: dark) { + body { + background: + radial-gradient(1200px 600px at 20% 10%, rgba(13, 110, 253, .25), transparent 60%), + radial-gradient(800px 400px at 80% 90%, rgba(32, 201, 151, .25), transparent 60%), + linear-gradient(180deg, #0b1020, #0e1326); + } + } + + .glass-card { + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); + background: rgba(255, 255, 255, .65); + border: 1px solid rgba(255, 255, 255, .5); + } + + [data-bs-theme="dark"] .glass-card { + background: rgba(17, 20, 34, .6); + border: 1px solid rgba(255, 255, 255, .08); + } + + /* 标题行 */ + .section-title { + display: flex; + align-items: center; + gap: .6rem; + margin: 0; + } + + .hint { + font-size: .9rem; + opacity: .75 + } + + /* 画布容器:响应式 16:9,可根据需要改比例 */ + .chart-wrap { + position: relative; + width: 100%; + aspect-ratio: 16 / 9; + /* 最小高度,防止数据少时过扁 */ + min-height: 280px; + } + + /* 让 canvas 完整填充容器(不改 id,仅加样式) */ + #lossChart { + position: absolute; + inset: 0; + width: 100% !important; + height: 100% !important; + display: block; + } + + /* “无结果”提示:沿用 id,不改逻辑,仅美化 */ + #none-exist { + margin: 0 0 1rem 0; + } + + + + +
+
+
+

+ 训练结果预览 +

+
Loss / Metric 曲线将根据你的训练日志自动绘制
+
+ + + + + +
+ +
+ + +
+ 提示:如果你使用 Chart.js,建议设置 responsive: truemaintainAspectRatio: false, + 本页样式已自动保证画布自适应容器尺寸。 +
+
+
+ + + + + + + + + + + + \ No newline at end of file