fix: GUI 模型加载改为按 metric 遍历所有 .joblib 选最优

旧行为: _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
This commit is contained in:
duxin
2026-08-04 09:10:34 +08:00
parent 04f9a647d8
commit c6523cebd8

View File

@ -233,6 +233,14 @@ class Step9MlPredictPanel(QWidget):
return return
self.external_model_dir = dir_path 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 = {} models_found = {}
errors = [] errors = []
@ -243,26 +251,53 @@ class Step9MlPredictPanel(QWidget):
if not subentry.is_dir(): if not subentry.is_dir():
continue continue
subdir_name = subentry.name subdir_name = subentry.name
joblib_files = [ joblib_files = sorted([
f for f in os.scandir(subentry.path) f for f in os.scandir(subentry.path)
if f.is_file() and f.name.lower().endswith(".joblib") if f.is_file() and f.name.lower().endswith(".joblib")
] ], key=lambda x: x.name)
if not joblib_files: if not joblib_files:
continue continue
joblib_path = joblib_files[0].path
try: # ★ 遍历该子目录下全部 .joblib 文件,按 metric 选出最佳模型
loaded = joblib.load(joblib_path) best_score = -float('inf') if _is_r2 else float('inf')
if isinstance(loaded, dict) and "model" in loaded: best_entry = None
# ★ 保留完整 dict含 metadata / train_wavelengths best_fname = None
# 推理端需要 train_wavelengths 做光谱重采样
models_found[subdir_name] = loaded for f_entry in joblib_files:
elif hasattr(loaded, "predict"): try:
models_found[subdir_name] = loaded data = joblib.load(f_entry.path)
else: if not isinstance(data, dict) or "model" not in data:
errors.append(f"{subdir_name}: 无法识别的格式 {type(loaded).__name__}") 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 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: except Exception as e:
QMessageBox.warning( QMessageBox.warning(