fix: DualStream_MNF 推理重建113列后交给 Pipeline.predict() 完整处理

- 之前手动调 imputer/preproc.transform 后返回, predict() 又套一层
  Pipeline → SimpleImputer 收到44列不匹配113列
- 改为重建113列训练特征集,返回后让 model.predict() 完整走
  Pipeline: imputer→preproc→cleaner→SVR,与训练完全一致
This commit is contained in:
duxin
2026-07-29 10:58:26 +08:00
parent c9374a489b
commit 3414f0207a

View File

@ -477,17 +477,15 @@ class WaterQualityInference:
metadata = self.loaded_model_data.get('metadata', {}) metadata = self.loaded_model_data.get('metadata', {})
# ═══════════════════════════════════════════════════════════ # ═══════════════════════════════════════════════════════════
# ★ DualStream_MNF 快速通道Pipeline 已内置 MNF + 物理指数 # ★ DualStream_MNF重建训练特征空间308→113列
# 但需先将推理光谱重采样到训练特征空间308→113列 # 让 Pipeline.predict() 完整走 imputer→preproc→cleaner→SVR
# 再走 Pipeline 的 imputer+preproc 步骤
# ═══════════════════════════════════════════════════════════ # ═══════════════════════════════════════════════════════════
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 完整处理")
# 1) 重采样 308 波段 → 训练波长网格
train_wl = metadata.get('train_wavelengths', None) train_wl = metadata.get('train_wavelengths', None)
train_cols = metadata.get('train_columns', None) train_cols = metadata.get('train_columns', None)
if train_wl is not None and len(train_wl) > 0 and train_cols is not None: if train_wl is not None and len(train_wl) > 0 and train_cols is not None:
# 从 spectra 中提取光谱列(列名可转 float 的即为波长列) # 1) 重采样 308 → 训练波长
spec_cols = [] spec_cols = []
for c in spectra.columns: for c in spectra.columns:
try: try:
@ -502,34 +500,27 @@ 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)
# 2) 计算 WQI 补齐到训练列数
wl_col_names = [f'{wl:.6f}' for wl in train_wl] wl_col_names = [f'{wl:.6f}' for wl in train_wl]
spectra_df = pd.DataFrame(resampled, columns=wl_col_names) spectra = pd.DataFrame(resampled, columns=wl_col_names)
# 2) 计算 WQI + 补零到训练列数
n_need = len(train_cols) n_need = len(train_cols)
if spectra_df.shape[1] < n_need: if spectra.shape[1] < n_need:
print(f"[DualStream_MNF] 计算 WQI 补齐: {spectra_df.shape[1]}{n_need}") print(f"[DualStream_MNF] 计算 WQI: {spectra.shape[1]}{n_need}")
try: try:
from src.utils.water_index import WaterQualityIndexCalculator from src.utils.water_index import WaterQualityIndexCalculator
calc = WaterQualityIndexCalculator() wqi = WaterQualityIndexCalculator().calculate_many(
formulas = calc.list_available() WaterQualityIndexCalculator().list_available(),
wqi = calc.calculate_many(formulas, spectra_df, fast=True) spectra, fast=True)
spectra_df = pd.concat([spectra_df, wqi], axis=1) spectra = pd.concat([spectra, wqi], axis=1)
except Exception as e: except Exception as e:
print(f"[DualStream_MNF] WQI 失败: {e}") print(f"[DualStream_MNF] WQI 失败: {e}")
# 如果还不够,补零 while spectra.shape[1] < n_need:
while spectra_df.shape[1] < n_need: spectra[f'_pad_{spectra.shape[1]}'] = 0.0
spectra_df[f'_pad_{spectra_df.shape[1]}'] = 0.0 spectra = spectra.iloc[:, :n_need]
spectra_df = spectra_df.iloc[:, :n_need] print(f"[DualStream_MNF] 特征重建完成: {spectra.shape}")
# 3) 喂入 Pipeline print(f"[特征对齐] 最终输入维度: {spectra.shape}")
spectra_processed = model.named_steps['imputer'].transform(spectra_df.values) # 返回 DataFrame后续 model.predict() 走完整 Pipeline
spectra_processed = model.named_steps['preproc'].transform(spectra_processed) return spectra.values
else:
spectra_processed = model.named_steps['imputer'].transform(spectra.values)
spectra_processed = model.named_steps['preproc'].transform(spectra_processed)
spectra_processed = np.nan_to_num(spectra_processed, nan=0.0, posinf=0.0, neginf=0.0)
print(f"[DualStream_MNF] 预处理完成: {spectra_processed.shape}")
print(f"[特征对齐] 最终输入维度: {spectra_processed.shape}")
return spectra_processed
train_wavelengths = metadata.get('train_wavelengths', None) train_wavelengths = metadata.get('train_wavelengths', None)
# 旧模型无 train_wavelengths → 不做波长匹配,走下方分支 B 的 linspace 路径 # 旧模型无 train_wavelengths → 不做波长匹配,走下方分支 B 的 linspace 路径