351 lines
15 KiB
Markdown
351 lines
15 KiB
Markdown
# 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²=…`
|
||
- [ ] `<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
|
||
|
||
```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` 槽) | 弹出**二次确认窗**,文案显示:<br>• 掩膜:`未指定(将自动生成 NDWI 水域掩膜)`<br>• 去耀斑:开启<br>• 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 描述 / 验收单:
|
||
|
||
```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 用于鲁棒性抽查。
|