# Smoke Test — 路线 B MVP(PipelineContext + AutoML + 软取消 + GUI 缝合) > 适用范围:路线 B 重构 4 部分(pipeline 包 / AutoML 训练器 / WorkerThread 软取消 / GUI 一键全自动)落盘后的端到端点火试飞清单。 > 目标:**用最小数据集(1 个 BSQ + 1 个 CSV)在 10–20 分钟内验证全链路打通**。 --- ## 0. 前置准备(5 分钟) ### 0.1 装 Optuna `environment.yml` 当前**未列** optuna(属于本次重构新增依赖)。若不装,Step 6 会自动降级到老 GridSearchCV(仍能跑通,但会触发 fallback 日志)。 ```bash call venv\Scripts\activate.bat pip install "optuna>=3.6,<4.0" ``` 写入 `environment.yml` 的 patch(提交时改): ```yaml # 路线 B AutoML 防爆引擎(可选;未装时 Step 6 走老 GridSearchCV 降级路径) - optuna>=3.6 ``` ### 0.2 准备最小数据集 ```text work_dir_smoke/ ├── raw/ │ ├── sample.b # 假彩色 BSQ(任意小分辨率都行,建议 50×50×6 波段) │ ├── sample_mask.tif # (可选)水域掩膜;不提供则 Step 1 自动生成 NDWI │ └── sample.csv # 含 3–6 个水质参数目标列(Chl-a / TSS / SD / TN / TP / COD…)+ 6 列波段反射率 └── (其他文件由流程自动生成) ``` **CSV 模板示例**(`feature_start_column` 默认为第一列;目标列必须**在特征列之前**): ```csv Chl-a,TSS,SD,B1,B2,B3,B4,B5,B6 12.3,15.1,0.8,0.045,0.052,0.038,0.061,0.072,0.085 11.8,14.2,0.9,0.044,0.051,0.037,0.060,0.071,0.084 ... (≥ 200 行;AutoML 智能子采样 N>5000 时才生效) ``` ### 0.3 启动 venv ```bash cd /d "D:\111\office\ZHLduijie\1.WQ\WQ_GUI" call venv\Scripts\activate.bat set PYTHONPATH=src;%PYTHONPATH% ``` --- ## 1. CLI 烟雾(最快路径,3 分钟)— **A 级:必跑** 跳过 GUI,直接验证 `automl_trainer.py` 自身可独立运行 + Optuna 子采样 + 降级路径: ```bash python -m src.core.prediction.automl_trainer ^ --csv work_dir_smoke/raw/sample.csv ^ --feature-start 6 ^ --n-trials 5 ^ --timeout 60.0 ^ --out work_dir_smoke/7_Supervised_Model_Training_AutoML ``` **通过标准**: - [ ] 进程退出码 0 - [ ] 控制台打印 `AutoML: 目标列 X 共尝试 N 个 trial,最佳 CV R²=…` - [ ] `//___AUTOML.joblib` 存在 - [ ] `/automl_summary.json` 存在且 `success=true` **若 Optuna 未装**,期待看到: ``` [AutoML] optuna 未安装,全目标列回退老 GridSearchCV ``` 产物文件名带 `_AUTOML` 后缀的逻辑此时**不会触发**(fallback 走老路径),属正常。 --- ## 2. GUI 端到端 9 步(核心场景,10–20 分钟)— **S 级:必跑** ### 2.1 启动 GUI ```bash call venv\Scripts\activate.bat set PYTHONPATH=src;%PYTHONPATH% python -m src.gui.water_quality_gui ``` ### 2.2 UI 配置 | 步骤 | 操作 | 期望 | | ----- | -------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | | 1/9 | 点"选择工作目录" → 选 `work_dir_smoke/` | 左侧步骤列表高亮,UI 不报错 | | 2/9 | 在 Step 1 面板选 `sample.b`;**掩膜留空**(验证 NDWI 自动生成路径) | 掩膜文本框保持空白 | | 3/9 | 在 Step 4 面板选 `sample.csv` | CSV 路径显示正确 | | 4/9 | **关键**:其他步骤(2/3/5/5.5/6/7/8/9)保持默认,不改任何参数 | AutoML 默认开启(use_automl=True) | | 5/9 | 点 **▶ 运行完整流程**(不要用老 `run_full_pipeline` 槽) | 弹出**二次确认窗**,文案显示:
• 掩膜:`未指定(将自动生成 NDWI 水域掩膜)`
• 去耀斑:开启
• AutoML:开启(Optuna 子采样寻优) | | 6/9 | 点"是(Y)" | "运行"按钮变灰,"停止"按钮亮起;进度条归零 | ### 2.3 观察日志(重点 4 大检查点) #### ✅ 检查点 1:ctx 路径传递 启动后**第一秒**应看到类似: ``` [Runner] ctx 已构造:14 路径字段,4 目录字段 [Runner] 步骤 1/14:step1_generate_water_mask(requires=['raw_img_path', 'water_mask_path']) [Runner] 步骤 2/14:step2_find_glint_area(requires=['raw_img_path', 'water_mask_path', 'output_dir']) ... [Runner] ctx 路径校准:water_mask_path = ...\work_dir_smoke\2_Glint_Area_Mask\glint_mask.tif ``` → **若没有 `[Runner]` 日志**,说明 v1 旧路径被走到了,**`inspect.signature` duck-type 没探测到 v2**,回去检查 `worker_thread.py:run()`。 #### ✅ 检查点 2:Step 1 NDWI 自动生成 ``` [Step1] 未指定 mask_path,自动基于 NDWI 生成水域掩膜 [Step1] NDWI 阈值=0.4,写入 1_Water_Mask/water_mask.tif ``` → 验证 `/1_Water_Mask/water_mask.tif` 文件存在且非空。 #### ✅ 检查点 3:AutoML 启用 ``` [Step6] AutoML 启用 Optuna 子采样寻优(timeout=300s, n_trials=20, max_samples=5000) [Step6] 目标列 'Chl-a' 共 3 个候选模型,最佳 R²=0.812(model=RandomForest) [Step6] 目标列 'TSS' 共 3 个候选模型,最佳 R²=0.745(model=XGBoost) [Step6] 训练完成,产物写入 7_Supervised_Model_Training_AutoML/ [Step6] automl_summary.json 写入完成 ``` → 验证产物: - [ ] `7_Supervised_Model_Training_AutoML//___AUTOML.joblib` ≥ 1 个 - [ ] `7_Supervised_Model_Training_AutoML/automl_summary.json` 含 `automl: true` 字段 - [ ] 老目录 `7_Supervised_Model_Training/` **不应该被创建**(AutoML 路径独立) #### ✅ 检查点 4:AutoML 降级(仅未装 Optuna 时) ``` [AutoML] optuna 未安装,全目标列回退老 GridSearchCV [Step6] 降级路径:调用 WaterQualityModelingBatch.train_models_batch(132 组 GridSearchCV) ``` → 跑通即可(仍能产生模型文件),但**降级**属于非优选路径。 ### 2.4 9 步全程观察清单 | 步 | 期望产物(路径相对 `work_dir`) | 期望耗时(50×50 测试数据) | | ---- | -------------------------------------------------------------- | -------------------------- | | 1 | `1_Water_Mask/water_mask.tif` | < 5 s | | 2 | `2_Glint_Area_Mask/glint_mask.tif` | < 5 s | | 3 | `3_Remove_Glint_Image/deglint_image.tif` | < 5 s | | 4 | `4_Process_CSV/processed_data.csv` | < 2 s | | 5 | `5_Training_Sample/training_spectra.csv` | < 5 s | | 5.5 | `5_5_Calculate_Indices/indices.csv`(如启用) | < 2 s | | **6**| `7_Supervised_Model_Training_AutoML/`(**新路径!**) | **< 5 min(Optuna 5 trial)** | | 6.5 | `6_5_Non_Empirical_Modeling/`(如启用) | 1–2 min | | 6.75 | `6_75_Custom_Regression/`(如启用) | 1–2 min | | 7 | `7_Sampling_Points/sampling_points.csv` | < 3 s | | 8 | `8_Prediction/predicted_values.csv` | < 5 s | | 8.5 | `8_5_Prediction_Non_Empirical/predicted.csv`(如启用) | < 5 s | | 8.75 | `8_75_Prediction_Custom/predicted.csv`(如启用) | < 5 s | | 9 | `9_Kriging_Distribution_Map/distribution_map.tif` | 5–30 s(纯 Python 慢) | ### 2.5 流程结束 - [ ] 进度条到 100% - [ ] "运行"按钮恢复可点 - [ ] "停止"按钮变灰 - [ ] 日志末行出现 `=== 流程执行完成 ===` 或 `=== 流程被取消 ===`(取决于是否点过停止) - [ ] 控制台 `on_pipeline_finished` 触发:UI 状态被统一恢复 --- ## 3. 软取消测试(3 分钟)— **A 级:必跑** 验证 `threading.Event` 软取消链路(不再用 `terminate()`)。 ### 3.1 启动完整流程 如 2.2 启动流程。 ### 3.2 中途点"停止" **时机**:在 Step 6 AutoML 跑 trials 的中途(看到 `[Step6] 目标列 'Chl-a' 共 N 个候选模型` 之后任意时刻)点"停止"。 **期望看到**: ``` [STOP] 用户请求软取消 [Step6] 检测到 cancel_event,本 trial 完成后退出 [Step6] AutoML 在 trial #X 中止,已完成 5/20 trial [Runner] 软取消:跳过剩余 8 个 step === 流程被取消 === ``` UI 状态: - [ ] "运行"按钮重新亮起 - [ ] "停止"按钮变灰 - [ ] 进度条保留在中断时的百分比(**不**归零) - [ ] `on_pipeline_finished` 触发(用 `success=False, cancelled=True` 区分) - [ ] **Python 进程不退出**(GUI 仍可继续点"运行"开新流程) **反例(不应该发生)**: - ❌ `QThread: Destroyed while thread is still running` 警告 - ❌ Python 解释器直接崩溃 - ❌ UI 永远卡死(`run_all_btn` 一直是灰的) ### 3.3 旧 `stop()` 路径回归 为防老代码忘了改,临时把 `water_quality_gui.py:stop_pipeline` 改回 `self.worker.stop()`,跑一次完整流程,看是否出现: ``` [DEPRECATED] WorkerThread.stop() 已弃用,请改用 soft_stop()。 ``` **这是预期行为**(弃用方法保留但打 warning),流程仍能完成即视为通过。 --- ## 4. 失败 / 降级场景(5 分钟)— **B 级:选跑** ### 4.1 未填掩膜 + NDWI 阈值设极端值 把 NDWI 阈值设到 `0.9`(几乎无水域),Step 1 应给出 warning 但不崩: ``` [Step1] NDWI 阈值=0.9,水域覆盖率 < 1%,请检查影像 ``` ### 4.2 CSV 完全无目标列 准备一个**没有目标列的 CSV**(全特征列),点运行: ``` [AutoML] 训练 CSV 不存在或无目标列:未识别出目标列 [Step6] AutoML 全部失败,所有目标列返回 success=False ``` → UI 不会崩,会在 `automl_summary.json` 写 `error: "未识别出目标列"`。 ### 4.3 Step 1 路径不存在 Step 1 选了一个**不存在的 .bsq 文件**: ``` [Runner] step1_generate_water_mask 异常:FileNotFoundError [STOP] 流程中止在 step 1 ``` → UI 弹错误窗 + 把左侧步骤列表 `setCurrentRow(0)` 自动定位到 Step 1(`_focus_step` 起效)。 ### 4.4 Optuna 版本冲突 装一个 `optuna==2.10`(API 大改),跑 GUI: ``` [AutoML] optuna API 不兼容(>=3.6 要求): [AutoML] 全目标列回退老 GridSearchCV ``` → 降级路径生效即视为通过。 --- ## 5. 验证矩阵 Checklist 复制以下到 PR 描述 / 验收单: ```markdown ## 路线 B MVP 验证矩阵 ### 代码落盘 - [ ] src/core/pipeline/__init__.py(17 行,4 export) - [ ] src/core/pipeline/context.py(PipelineContext dataclass) - [ ] src/core/pipeline/runner.py(StepSpec + PIPELINE_STEPS + PipelineRunner) - [ ] src/core/prediction/__init__.py(追加 train_with_automl export) - [ ] src/core/prediction/automl_trainer.py(AutoMLResult + train_with_automl + CLI) - [ ] src/core/steps/modeling_step.py(use_automl 分支 + _train_models_automl) - [ ] src/core/water_quality_inversion_pipeline_GUI.py(run_full_pipeline_v2 + LEGACY_ATTR_MAP + _sync_legacy_attrs_from_context) - [ ] src/gui/core/worker_thread.py(cancel_event + soft_stop + run() duck-type) - [ ] src/gui/water_quality_gui.py(on_run_all_clicked + _collect_minimal_config + 按钮重连) ### CLI 自测 - [ ] A.1 `python -m src.core.prediction.automl_trainer --csv ...` 退出码 0 - [ ] A.2 产物 .joblib 含 `_AUTOML` 后缀 - [ ] A.3 automl_summary.json 含 success=true ### GUI 端到端 - [ ] B.1 启动无 ImportError - [ ] B.2 二次确认窗文案含 mask 提示 + AutoML 状态 - [ ] B.3 日志含 [Runner] 前缀(v2 路径生效) - [ ] B.4 Step 1 NDWI 自动生成路径生效 - [ ] B.5 9 步产物路径全部存在 - [ ] B.6 流程结束后 UI 状态恢复(运行按钮亮、停止按钮灰) ### 软取消 - [ ] C.1 流程中途点停止,cancel_event 触发 - [ ] C.2 流程被取消而非崩溃 - [ ] C.3 UI 状态由 on_pipeline_finished 统一恢复 - [ ] C.4 旧 stop() 调用打 [DEPRECATED] warning ### 降级 - [ ] D.1 Optuna 未装 → 全目标列回退老 GridSearchCV - [ ] D.2 无目标列 CSV → 写 error 到 summary,不崩 UI - [ ] D.3 不存在文件 → _focus_step 定位到对应 step ``` --- ## 6. 已知未做(不在本次范围) - [ ] Kriging 多进程并行(当前 backend="loop" 纯 Python) - [ ] Step 5 radius==0 内存优化(整波段读入) - [ ] 进度条 sub-step 粒度(当前只到 step 级) - [ ] Step 8 全图预测(当前只对采样点预测) - [ ] 全项目搜替换老 `self.worker.stop()` 调用(仅本会话改了 `water_quality_gui.py` 的 stop_pipeline) - [ ] `requirements.txt` 同步 Optuna(仅 `environment.yml` 写) - [ ] 单元测试套件(`tests/` 目录为空;建议用 pytest 覆盖 train_with_automl / PipelineRunner) --- ## 7. 出问题找哪里 | 现象 | 看哪里 | | --------------------------------------------- | ------------------------------------------------------- | | `[Runner]` 日志没出来 | `worker_thread.py:run()` 的 `inspect.signature` 探测 | | `[AutoML]` 完全没打 | `modeling_step.py:170` 的 `if use_automl` 是否进了 | | AutoML 报 `optuna API 不兼容` | `automl_trainer.py:236` 的 `try import` 块 | | 软取消无反应 | `worker_thread.py:run()` 末尾的 `cancel_event.is_set()` | | 二次确认窗没出来 | `water_quality_gui.py:on_run_all_clicked` line ~2848 | | 9 步产物路径错位 | `pipeline/runner.py:PIPELINE_STEPS` 的 `output` 字段 | | 老 v1 路径被走到 | `_sync_legacy_attrs_from_context` 没调,或 v2 异常 | --- > **作者注**:本清单对应**路线 B 一键全自动重构 4 部分全部落盘**的验收场景,编号与 todo 8 同步。 > 跑通 §1 + §2 + §3 三段即视为 MVP 验收通过;§4 用于鲁棒性抽查。