feat: ML模型波长记忆 + 跨传感器光谱重采样 + 数据加载规范化

训练端 (modeling_batch.py):
- _extract_train_wavelengths() 从列名提取训练波长写入 metadata
- load_data_batch/load_data_single 改为基于列名语义智能提取特征
- 废除 feature_start_column 硬编码位置索引

推理端 (inference_batch.py):
- preprocess_spectra() 分支A: train_wavelengths存在→np.interp精确重采样
- preprocess_spectra() 分支B: 无波长元数据→解析列名→np.interp到400-800nm标准网格
- 废除暴力截断和零值填充
This commit is contained in:
duxin
2026-07-27 14:52:30 +08:00
parent 99aeab3076
commit 92e8c90370
7 changed files with 398 additions and 149 deletions

View File

@ -14,7 +14,7 @@ Step8 后端计算服务(机器学习建模训练)
execute_step8({
"training_csv_path": "D:/training_spectra_indices.csv", # 训练 CSV(必填)
"feature_start_column": "374.285004", # 特征起始列名/索引
"feature_start_column": None, # None=智能识别特征列
"preprocessing_methods": ["None", "MMS"], # 预处理方法列表
"model_names": ["RF", "SVR", "Ridge", "Lasso"], # 模型列表
"split_methods": ["spxy"], # 划分方法列表
@ -68,7 +68,7 @@ def execute_step8(config: Dict[str, Any]) -> Dict[str, Any]:
"""
# ---------- 入参规整 ----------
training_csv_path: Optional[str] = config.get("training_csv_path")
feature_start_column: str = str(config.get("feature_start_column", "374.285004"))
feature_start_column: Optional[str] = config.get("feature_start_column", None)
preprocessing_methods: Optional[List[str]] = config.get("preprocessing_methods")
model_names: Optional[List[str]] = config.get("model_names")
split_methods: Optional[List[str]] = config.get("split_methods")

View File

@ -104,12 +104,12 @@ class Step8View(BaseView):
params_layout = QFormLayout()
self.feature_start = QLineEdit()
self.feature_start.setText("374.285004")
self.feature_start.setPlaceholderText("留空=智能识别波长列和WQI列")
params_layout.addRow("特征起始列:", self.feature_start)
feature_start_hint = QLabel(
"提示:请使用记事本打开 training_spectra.csv 确认首个波长的精确表头名称"
"(如 374.285 或 374.285004)并在此填入,避免因浮点精度差异导致列名匹配失败。"
"提示:留空则自动识别所有波长列(纯数字列名)和 WQI 指数列作为特征。\n"
"如需手动指定,可填入首个波长列名(如 374.285004)或列索引。"
)
feature_start_hint.setWordWrap(True)
feature_start_hint.setStyleSheet("color: #666; font-size: 10px;")
@ -253,7 +253,7 @@ class Step8View(BaseView):
]
config = {
"feature_start_column": self.feature_start.text(),
"feature_start_column": self.feature_start.text().strip() or None,
"preprocessing_methods": preprocessing_methods if preprocessing_methods else ["None"],
"model_names": model_names if model_names else ["SVR"],
"split_methods": split_methods if split_methods else ["random"],
@ -270,7 +270,8 @@ class Step8View(BaseView):
def set_config(self, config: dict):
if "feature_start_column" in config:
self.feature_start.setText(str(config["feature_start_column"]))
val = config["feature_start_column"]
self.feature_start.setText(str(val) if val is not None else "")
if "cv_folds" in config:
self.cv_folds.setValue(config["cv_folds"])
if "preprocessing_methods" in config: