15 KiB
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 日志)。
call venv\Scripts\activate.bat
pip install "optuna>=3.6,<4.0"
写入 environment.yml 的 patch(提交时改):
# 路线 B AutoML 防爆引擎(可选;未装时 Step 6 走老 GridSearchCV 降级路径)
- optuna>=3.6
0.2 准备最小数据集
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 默认为第一列;目标列必须在特征列之前):
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
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 子采样 + 降级路径:
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²=… <out>/<preprocess>/<target>_<preprocess>_<model>_AUTOML.joblib存在<out>/automl_summary.json存在且success=true
若 Optuna 未装,期待看到:
[AutoML] optuna 未安装,全目标列回退老 GridSearchCV
产物文件名带 _AUTOML 后缀的逻辑此时不会触发(fallback 走老路径),属正常。
2. GUI 端到端 9 步(核心场景,10–20 分钟)— S 级:必跑
2.1 启动 GUI
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
→ 验证 <work_dir>/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/<preprocess>/<target>_<preprocess>_<model>_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 要求):<error>
[AutoML] 全目标列回退老 GridSearchCV
→ 降级路径生效即视为通过。
5. 验证矩阵 Checklist
复制以下到 PR 描述 / 验收单:
## 路线 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 用于鲁棒性抽查。