diff --git a/docs/technical-selection-progress-report.md b/docs/technical-selection-progress-report.md new file mode 100644 index 0000000..0751b2b --- /dev/null +++ b/docs/technical-selection-progress-report.md @@ -0,0 +1,231 @@ +# 技术选型与当前进度报告 + +> 报告生成时间:2026-09-11 +> 基线提交:`1b30fdd docs: add development progress report` +> 仓库验证:`uv run pytest -q` 通过 25 个测试;`uv run ruff check .` 与 `uv run ruff format --check .` 均无错误。 + +## 1. 报告定位 + +本报告面向技术验收与项目推进评审,回答三个问题: + +1. 当前技术路线是否已经形成可验证的闭环。 +2. 当前交付物有哪些可量化证据。 +3. 距离后续训练与服务化还差哪些受控步骤。 + +当前阶段的核心结论是:项目已经从“技术方案文档”推进到“可运行的数据与模型工程原型”。外部数据平台的只读入口、特征预处理、目标拆分、模型路由、无泄漏验证和产物落盘都已经形成一条命令可复现的链路。 + +## 2. 仓库与验证状态 + +| 指标 | 当前数值 | 说明 | +| --- | ---: | --- | +| `src/lmpm` Python 代码行数 | 927 | 包含 `domain/`、`data/`、`training/`、`scripts/` | +| 测试代码行数 | 572 | 测试与源码行数比约 62% | +| 测试文件数 | 6 | 覆盖领域模型、存储、数据入口、预处理、目标、模型 | +| 测试用例数 | 25 | `uv run pytest -q` 全部通过 | +| 测试执行时间 | 3.72 s | 本地基线,不含远程/容器差异 | +| 生产依赖数量 | 5 | `joblib`、`pandas`、`pydantic`、`pydantic-settings`、`scikit-learn` | +| 开发依赖数量 | 2 | `pytest`、`ruff` | +| Ruff 错误数 | 0 | `uv run ruff check .` | +| 格式偏差文件数 | 0 | `uv run ruff format --check .` | +| 当前远程仓库数量 | 2 | `origin` 与 `ubuntu` | + +## 3. 当前提交轨迹 + +| 提交 | 内容 | 意义 | +| --- | --- | --- | +| `9a9804d` | 初始化仓库 | 建立架构文档与仓库起点 | +| `3ebc097` | 数据层骨架 | 建立 Pydantic 领域模型与本地 SQLite 骨架 | +| `7bdd705` | Ubuntu/Docker 部署策略 | 明确生产与训练运行环境 | +| `272542b` | 只读训练数据加载器 | 打通外部 `material_records` 表 | +| `947fd12` | 目标拆分 | 将 6 个结果字段分成分类/回归 | +| `df3ab51` | 预处理与模型路由 | 建立可复用特征策略和目标级模型选择 | +| `8e0b371` | 模型训练流水线 | 完成 LOMO 验证、训练、落盘 | +| `1b30fdd` | 进度报告 | 将当前状态文档化 | + +这条轨迹显示当前不是单纯堆代码,而是按照“数据契约 → 特征契约 → 目标契约 → 模型契约 → 验证与产物”的顺序推进。 + +## 4. 当前数据契约规模 + +外部平台中的 `material_records` 是训练数据的唯一事实源。当前训练契约由 `src/lmpm/training/dataset.py` 固定: + +| 数据块 | 数量 | 组成 | +| --- | ---: | --- | +| metadata | 4 | `experiment_id`、`test_date`、`material_category`、`material_name` | +| numeric features | 18 | 材料物性与激光工艺参数 | +| categorical features | 2 | `filling_method`、`processing_size` | +| total features | 20 | 18 个数值列 + 2 个类别列 | +| classification targets | 2 | `is_cut_through`、`is_fire_smolder` | +| regression targets | 4 | `carbonized_edge_width`、`etching_depth`、`pattern_clarity_score`、`presentation_balance_score` | +| total targets | 6 | 2 个布尔结果 + 4 个连续结果 | + +## 5. 外部数据平台当前证据 + +当前检查的平台备份文件为: + +```text +/Users/broccoli/Documents/db/backups/database-20260718-134620.sqlite3 +``` + +该备份中的 `material_records` 表: + +| 项目 | 数值 | +| --- | ---: | +| 表总数 | 6 | +| `material_records` 记录数 | 1 | +| 表字段数 | 35 | +| 非空工艺字段 | `actual_output_power`、`display_current`、`scanning_speed`、`pulse_frequency`、`pulse_width`、`defocus_amount`、`scan_line_spacing` | + +备份中的样例记录显示: + +| 字段 | 数值 | +| --- | ---: | +| `material_name` | 椴木板 | +| `material_category` | 木材 | +| `actual_output_power` | 8.5 | +| `scanning_speed` | 100.0 | +| `pulse_frequency` | 20.0 | +| `pulse_width` | 5.0 | +| `defocus_amount` | 0.0 | +| `scan_line_spacing` | 0.1 | +| `is_cut_through` | 1 | +| `carbonized_edge_width` | 0.2 | +| `etching_depth` | 30.0 | +| `is_fire_smolder` | 0 | +| `pattern_clarity_score` | 9.0 | +| `presentation_balance_score` | 8.0 | + +这说明外部平台的表结构已经足以支撑训练入口读取。虽然真实训练数据尚未成规模,但“外部数据库 → 仓库训练契约”的接口路径已经不再是纸面假设。 + +## 6. 技术选型与理由 + +### 6.1 保留的技术路线 + +| 决策项 | 当前选择 | 理由 | +| --- | --- | --- | +| 语言运行时 | Python 3.10+ | 科学计算与表格建模生态成熟,团队维护成本可控 | +| 依赖管理 | `uv` + `uv.lock` | 安装速度快,锁定依赖版本,适合 Ubuntu/Docker 复现 | +| 数据建模 | Pydantic v2 | 在数据入口处强制字段类型和校验,减少脏数据扩散 | +| 数据存储 | SQLite | 500 组以下实验规模下零运维,且外部平台已使用该形态 | +| 特征处理 | pandas + scikit-learn | 表格数据的标准组合,避免早期引入过重依赖 | +| 模型序列化 | joblib | 保存 sklearn 对象和预处理器,便于快速验证与本地服务 | +| 测试 | pytest | 覆盖数据契约、错误路径和模型流程 | +| 静态检查 | Ruff | 当前 0 错误,保持代码风格和导入规范稳定 | + +### 6.2 当前基线模型 + +| 目标类型 | 模型 | 参数 | +| --- | --- | --- | +| regression | `GaussianProcessRegressor` | `RBF + WhiteKernel`,`normalize_y=True` | +| classification | `RandomForestClassifier` | `n_estimators=300`,`class_weight="balanced"`,`random_state=42` | + +选择 GPR 是因为它适合小样本连续回归,并且原生输出均值和方差,符合后续不确定性量化需求。选择随机森林是因为布尔结果对分布假设不敏感,在小样本和类别不平衡场景下更稳健。 + +当前没有立刻引入 XGBoost、TabPFN、MAPIE 或 Ax/BoTorch,这是刻意延后。原因是真实数据规模尚未到支持模型对比和主动学习的阶段,提前引入会增加依赖复杂度,却不能带来可验证收益。 + +## 7. 当前能力矩阵 + +| 能力 | 完成度 | 可验证证据 | +| --- | ---: | --- | +| 领域模型定义 | 5/5 | `MaterialProperty`、`ProcessingParams`、`QualityMetrics`、`ExperimentRecord` | +| 本地 SQLite 骨架 | 4/5 | `materials`、`experiments` 表可初始化和往返 | +| 外部平台只读入口 | 4/5 | `load_sqlite_training_dataset()` 可读 `material_records` | +| 目标契约 | 5/5 | 2 个分类目标 + 4 个回归目标 | +| 特征预处理 | 4/5 | 数值中位数填补 + 标准化;类别众数填补 + one-hot | +| 模型路由 | 4/5 | 连续目标 GPR,布尔目标随机森林 | +| LOMO 验证 | 3/5 | 已实现按材料名 held-out,但真实数据量不足 | +| 模型持久化 | 3/5 | joblib 模型 + JSON 指标报告 | +| 命令行入口 | 4/5 | `uv run python -m lmpm.scripts.train ` | +| 推理服务 | 0/5 | 尚未启动 | +| Docker 部署 | 1/5 | 已确定策略,但 Dockerfile 未落地 | +| 多模型对比 | 1/5 | 只落了 GPR/RF 基线,尚未做横向对比 | + +## 8. 与架构方案的对照 + +架构报告规划了 GPR、XGBoost、随机森林,并预留 TabPFN 作为第四候选。当前实现只先落地 GPR 与随机森林,这是有意缩小第一轮范围。 + +| 架构规划 | 当前状态 | 差异说明 | +| --- | --- | --- | +| GPR 为主 | 已实现 | 使用 `RBF + WhiteKernel`,`normalize_y=True` | +| 随机森林作为对比 | 已实现分类基线 | 300 棵树,类权重 balanced | +| XGBoost 对比 | 未接入 | 等真实数据规模足够后引入 | +| TabPFN 对比 | 未接入 | 仅作为后续候选,不作为主线依赖 | +| GPR 原生不确定性 | 部分具备 | 模型类型支持,但评估报告尚未输出预测区间 | +| Conformal Prediction | 未接入 | 等真实数据和统一评估协议稳定后再加 | +| LOMO 验证 | 已实现 | 当前按 `material_name` 分组 | +| ONNX + joblib 双格式 | 只实现 joblib | ONNX 精度对比延后到服务化阶段 | +| FastAPI 服务 | 未启动 | 先稳定训练和评估闭环 | +| Ubuntu/Docker | 策略确定 | 具体镜像与部署脚本未完成 | + +## 9. 已交付的核心文件 + +| 文件 | 行数 | 作用 | +| --- | ---: | --- | +| `src/lmpm/training/models.py` | 247 | 训练、LOMO 评估、持久化、恢复 | +| `src/lmpm/training/dataset.py` | 152 | 外部平台只读数据入口 | +| `src/lmpm/data/store.py` | 130 | 本地 SQLite 骨架 | +| `src/lmpm/training/preprocessing.py` | 129 | 统一特征处理策略 | +| `src/lmpm/training/targets.py` | 81 | 目标字段任务拆分 | +| `src/lmpm/training/routing.py` | 51 | 目标到模型族的路由 | +| `src/lmpm/scripts/train.py` | 36 | CLI 入口 | +| `src/lmpm/training/workflow.py` | 16 | 一步式训练调用 | +| `src/lmpm/training/pipeline.py` | 26 | 特征、目标、路由组装 | + +## 10. 验收人员可复现的检查方式 + +验收人员可以在仓库根目录直接运行: + +```bash +uv sync +uv run pytest -q +uv run ruff check . +uv run ruff format --check . +uv run python -m lmpm.scripts.train +``` + +前四条用于验证工程质量,最后一条用于验证从外部 SQLite 到模型产物的一步式链路。当前仓库已经具备将外部平台数据读入并产出模型与指标报告的能力。 + +## 11. 当前可控风险 + +| 风险 | 当前状态 | 处置策略 | +| --- | --- | --- | +| 真实数据不足 | 平台备份仅 1 条历史记录 | 先保持数据画像和训练闭环,不启动调参 | +| 模型精度不可承诺 | 当前指标只来自构造样例 | 以数据画像和横向基线对比为先 | +| GPR ONNX 精度风险 | 未进入导出阶段 | 先使用 joblib,后续导出时做 float64 对比 | +| 缺少训练元数据 | joblib 文件未绑定数据哈希/commit | 下一步加入 artifact metadata | +| 缺少 Dockerfile | 只有策略,无镜像 | 等训练入口稳定后补齐 | +| 推理链路未闭环 | 只有训练产物 | 后续补 `predict` 入口和 FastAPI 服务 | + +## 12. 下一步验收里程碑 + +1. **数据画像脚本** + - 输出真实数据库的行数、缺失率、目标分布、材料分组统计。 + - 这是进入模型调优前的最低门槛。 + +2. **训练元数据文件** + - 每次训练产物旁生成 `metadata.json`。 + - 记录代码 commit、数据哈希、特征顺序、模型参数、评估指标。 + +3. **多模型对比协议** + - 保持统一特征、统一 LOMO 分组、统一指标。 + - 真实数据达到一定规模后再引入 XGBoost、TabPFN、Conformal Prediction。 + +4. **Dockerfile 与远程训练** + - 依据当前 Ubuntu/Docker 策略固化运行环境。 + - 使本地与远程训练使用同一份 `uv.lock`。 + +5. **推理服务** + - 先提供本地 predict 入口,再封装 FastAPI。 + - 输入、输出、模型版本和不确定性指标一起返回。 + +## 13. 结论 + +当前项目已经完成从架构文档到可运行训练原型的实质转化。最核心的验收价值不在于模型精度已经成熟,而在于: + +1. 数据入口不再依赖手工整理。 +2. 特征和目标契约已经稳定。 +3. 六个结果字段被正确拆分为分类/回归任务。 +4. 训练流程已经有无泄漏验证和可复现产物。 +5. 工程质量由测试、Ruff、锁文件和文档共同保障。 + +因此,当前阶段的技术路线是成立的。下一步应当把资源集中在真实数据接入、数据画像和模型版本化上,而不是过早追求复杂模型。 +