From c6523cebd8829d0b7253e853a1aa735a00b9d748 Mon Sep 17 00:00:00 2001 From: duxin Date: Tue, 4 Aug 2026 09:10:34 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20GUI=20=E6=A8=A1=E5=9E=8B=E5=8A=A0?= =?UTF-8?q?=E8=BD=BD=E6=94=B9=E4=B8=BA=E6=8C=89=20metric=20=E9=81=8D?= =?UTF-8?q?=E5=8E=86=E6=89=80=E6=9C=89=20.joblib=20=E9=80=89=E6=9C=80?= =?UTF-8?q?=E4=BC=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 旧行为: _scan_external_model_dir 盲目取 joblib_files[0] → 字母序选到 LinearRegression 而非 SVR → 所有目标永远是 D1,因为字母序 D1 < SNV 新行为: - 读取 GUI 中用户选择的 metric (R²/RMSE/MAE) - 遍历每个子目录下全部 .joblib 文件 - 按指标自动选择最优模型: R² → 取最大值 RMSE → 取最小值 MAE → 取最小值 - 控制台打印: [模型加载] 目标 BGA 根据 R² 自动选择最佳模型: BGA_spxy_SNV_SVR.joblib, 分数: 0.899001 --- src/gui/panels/step9_ml_predict_panel.py | 65 ++++++++++++++++++------ 1 file changed, 50 insertions(+), 15 deletions(-) diff --git a/src/gui/panels/step9_ml_predict_panel.py b/src/gui/panels/step9_ml_predict_panel.py index dfe7217..f69e93f 100644 --- a/src/gui/panels/step9_ml_predict_panel.py +++ b/src/gui/panels/step9_ml_predict_panel.py @@ -233,6 +233,14 @@ class Step9MlPredictPanel(QWidget): return self.external_model_dir = dir_path + + # ★ 读取用户选择的模型评估指标 + _metric_key = self.metric.currentData() # "test_r2" / "test_rmse" / "test_mae" + _metric_display = self.metric.currentText() # "R² (决定系数)" / ... + _is_r2 = 'r2' in _metric_key.lower() + _is_rmse = 'rmse' in _metric_key.lower() + _is_mae = 'mae' in _metric_key.lower() + models_found = {} errors = [] @@ -243,26 +251,53 @@ class Step9MlPredictPanel(QWidget): if not subentry.is_dir(): continue subdir_name = subentry.name - joblib_files = [ + joblib_files = sorted([ f for f in os.scandir(subentry.path) if f.is_file() and f.name.lower().endswith(".joblib") - ] + ], key=lambda x: x.name) if not joblib_files: continue - joblib_path = joblib_files[0].path - try: - loaded = joblib.load(joblib_path) - if isinstance(loaded, dict) and "model" in loaded: - # ★ 保留完整 dict(含 metadata / train_wavelengths), - # 推理端需要 train_wavelengths 做光谱重采样 - models_found[subdir_name] = loaded - elif hasattr(loaded, "predict"): - models_found[subdir_name] = loaded - else: - errors.append(f"{subdir_name}: 无法识别的格式 {type(loaded).__name__}") + + # ★ 遍历该子目录下全部 .joblib 文件,按 metric 选出最佳模型 + best_score = -float('inf') if _is_r2 else float('inf') + best_entry = None + best_fname = None + + for f_entry in joblib_files: + try: + data = joblib.load(f_entry.path) + if not isinstance(data, dict) or "model" not in data: + continue + meta = data.get('metadata', {}) + # 按指标优先级:test_xxx → train_xxx + score = None + if _is_r2: + score = meta.get('test_r2', meta.get('train_r2', None)) + elif _is_rmse: + score = meta.get('test_rmse', meta.get('train_rmse', None)) + elif _is_mae: + score = meta.get('test_mae', meta.get('train_mae', None)) + + if score is None: + continue + + if _is_r2 and score > best_score: + best_score = score + best_entry = data + best_fname = f_entry.name + elif not _is_r2 and score < best_score: + best_score = score + best_entry = data + best_fname = f_entry.name + except Exception: continue - except Exception as e: - errors.append(f"{subdir_name}: {type(e).__name__}: {e}") + + if best_entry is not None: + models_found[subdir_name] = best_entry + print(f"[模型加载] 目标 {subdir_name} 根据 {_metric_display} " + f"自动选择最佳模型: {best_fname}, 分数: {best_score:.6f}") + else: + errors.append(f"{subdir_name}: 无可评估的 .joblib 文件") except Exception as e: QMessageBox.warning(