refactor: DualStream 纯光谱输入 — 消除 WQI 冗余双向断层

训练端:
- DualStream_MNF 时只取纯光谱列(50列)传给 Pipeline
- PhysicalFeatureExtractor + MNFTransformer 都只收纯光谱
- 不再从 CSV 预读 WQI 列混入输入

推理端:
- 只做 308→50 光谱重采样,不补 WQI
- pipeline.predict() 自动完成 Physical 指数计算 + MNF 降维
- 删除 30+ 行 WQI 补齐代码

训练/推理完全对称: 纯光谱入 → FeatureUnion → SVR 出
This commit is contained in:
duxin
2026-07-29 13:15:10 +08:00
parent 6f1f222baa
commit f4a927386b
2 changed files with 19 additions and 26 deletions

View File

@ -606,7 +606,16 @@ class WaterQualityModelingBatch:
print(f"开始训练模型: {model_name} (预处理: {preprocess_method})") print(f"开始训练模型: {model_name} (预处理: {preprocess_method})")
# 使用指定方法分割训练集和测试集(用原始 X_raw,Pipeline 内置 transform 处理) # ═══════════════════════════════════════════════════════════
# ★ DualStream_MNF:只取纯光谱列,WQI 由 Pipeline 内 PhysicalExtractor 动态计算
# ═══════════════════════════════════════════════════════════
if preprocess_method == "DualStream_MNF":
_spec_cols = [c for c in X_raw.columns if self._is_wavelength_column(c)]
X_raw = X_raw[_spec_cols]
print(f"[DualStream_MNF] 精简为纯光谱: {X_raw.shape[1]} 列 "
f"({X_raw.columns[0]} ~ {X_raw.columns[-1]} nm)")
# 使用指定方法分割训练集和测试集
X_train, X_test, y_train, y_test = self.split_data( X_train, X_test, y_train, y_test = self.split_data(
X_raw, y, method=split_method, test_size=test_size, random_state=random_state X_raw, y, method=split_method, test_size=test_size, random_state=random_state
) )

View File

@ -477,15 +477,14 @@ class WaterQualityInference:
metadata = self.loaded_model_data.get('metadata', {}) metadata = self.loaded_model_data.get('metadata', {})
# ═══════════════════════════════════════════════════════════ # ═══════════════════════════════════════════════════════════
# ★ DualStream_MNF:重建训练特征空间(308→113列), # ★ DualStream_MNF:纯光谱 → 重采样到训练波长 → pipeline.predict()
# 让 Pipeline.predict() 完整走 imputer→preproc→cleaner→SVR # Pipeline 内置的 FeatureUnion[PhysicalExtractor + MNF]
# 自动计算物理指数和降维,无需外部补 WQI
# ═══════════════════════════════════════════════════════════ # ═══════════════════════════════════════════════════════════
if actual_preprocess_method == "DualStream_MNF" and isinstance(model, Pipeline): if actual_preprocess_method == "DualStream_MNF" and isinstance(model, Pipeline):
print("[DualStream_MNF] 推理:重建训练特征集,交给 Pipeline 完整处理") print("[DualStream_MNF] 推理:纯光谱重采样 → Pipeline 全自动处理")
train_wl = metadata.get('train_wavelengths', None) train_wl = metadata.get('train_wavelengths', None)
train_cols = metadata.get('train_columns', None) if train_wl is not None and len(train_wl) > 0:
if train_wl is not None and len(train_wl) > 0 and train_cols is not None:
# 1) 重采样 308 → 训练波长
spec_cols = [] spec_cols = []
for c in spectra.columns: for c in spectra.columns:
try: try:
@ -500,26 +499,11 @@ class WaterQualityInference:
resampled[i] = np.interp(dst_wl, src_wl, spec_data[i], resampled[i] = np.interp(dst_wl, src_wl, spec_data[i],
left=np.nan, right=np.nan) left=np.nan, right=np.nan)
resampled = np.nan_to_num(resampled, nan=0.0) resampled = np.nan_to_num(resampled, nan=0.0)
wl_col_names = [f'{wl:.6f}' for wl in train_wl] spectra = pd.DataFrame(resampled,
spectra = pd.DataFrame(resampled, columns=wl_col_names) columns=[f'{wl:.6f}' for wl in train_wl])
# 2) 计算 WQI + 补零到训练列数 print(f"[DualStream_MNF] 纯光谱重采样完成: {spectra.shape[1]} 列 "
n_need = len(train_cols) f"→ pipeline.predict() 将自动完成 Physical+MNF 变换")
if spectra.shape[1] < n_need:
print(f"[DualStream_MNF] 计算 WQI: {spectra.shape[1]} → {n_need}")
try:
from src.utils.water_index import WaterQualityIndexCalculator
wqi = WaterQualityIndexCalculator().calculate_many(
WaterQualityIndexCalculator().list_available(),
spectra, fast=True)
spectra = pd.concat([spectra, wqi], axis=1)
except Exception as e:
print(f"[DualStream_MNF] WQI 失败: {e}")
while spectra.shape[1] < n_need:
spectra[f'_pad_{spectra.shape[1]}'] = 0.0
spectra = spectra.iloc[:, :n_need]
print(f"[DualStream_MNF] 特征重建完成: {spectra.shape}")
print(f"[特征对齐] 最终输入维度: {spectra.shape}") print(f"[特征对齐] 最终输入维度: {spectra.shape}")
# 返回 DataFrame,后续 model.predict() 走完整 Pipeline
return spectra.values return spectra.values
train_wavelengths = metadata.get('train_wavelengths', None) train_wavelengths = metadata.get('train_wavelengths', None)