338 lines
30 KiB
Markdown
338 lines
30 KiB
Markdown
# 开发日志记录
|
||
|
||
本文档记录了项目开发过程中的主要修改、问题修复和重要决策。
|
||
|
||
---
|
||
|
||
## 2025-07-13:早期后端修复与重构
|
||
**开发者**: lyf
|
||
|
||
### 13:30 - 修复数据加载路径问题
|
||
- **任务目标**: 解决模型训练时因数据文件路径错误导致的数据加载失败问题。
|
||
- **核心问题**: `server/core/predictor.py` 中的 `PharmacyPredictor` 类初始化时,硬编码了错误的默认数据文件路径。
|
||
- **修复方案**: 将默认数据路径更正为 `'data/timeseries_training_data_sample_10s50p.parquet'`,并同步更新了所有训练器。
|
||
|
||
### 14:00 - 数据流重构
|
||
- **任务目标**: 解决因数据处理流程中断导致关键特征丢失,从而引发模型训练失败的根本问题。
|
||
- **核心问题**: `predictor.py` 未将预处理好的数据向下传递,导致各训练器重复加载并错误处理数据。
|
||
- **修复方案**: 重构了核心数据流,确保数据在 `predictor.py` 中被统一加载和预处理,然后作为一个DataFrame显式传递给所有下游的训练器函数。
|
||
|
||
---
|
||
|
||
## 2025-07-14:模型训练与并发问题集中攻坚
|
||
**开发者**: lyf
|
||
|
||
### 10:16 - 修复训练器层 `KeyError`
|
||
- **问题**: 所有模型训练均因 `KeyError: "['sales', 'price'] not in index"` 失败。
|
||
- **分析**: 训练器硬编码的特征列表中包含了数据源中不存在的 `'price'` 列。
|
||
- **修复**: 从所有四个训练器 (`mlstm`, `transformer`, `tcn`, `kan`) 的 `features` 列表中移除了对不存在的 `'price'` 列的依赖。
|
||
|
||
### 10:38 - 修复数据标准化层 `KeyError`
|
||
- **问题**: 修复后出现新错误 `KeyError: "['sales'] not in index"`。
|
||
- **分析**: `server/utils/multi_store_data_utils.py` 中的 `standardize_column_names` 函数列名映射错误,且缺少最终列选择机制。
|
||
- **修复**: 修正了列名映射,并增加了列选择机制,确保函数返回的 `DataFrame` 结构统一且包含 `sales` 列。
|
||
|
||
### 11:04 - 修复JSON序列化失败问题
|
||
- **问题**: 训练完成后,因 `Object of type float32 is not JSON serializable` 导致前后端通信失败。
|
||
- **分析**: 训练产生的评估指标是NumPy的 `float32` 类型,无法被标准 `json` 库序列化。
|
||
- **修复**: 在 `server/utils/training_process_manager.py` 中增加了 `convert_numpy_types` 辅助函数,在通过WebSocket或API返回数据前,将所有NumPy数值类型转换为Python原生类型,从根源上解决了所有序列化问题。
|
||
|
||
### 11:15 - 修复MAPE计算错误
|
||
- **问题**: 训练日志显示 `MAPE: nan%` 并伴有 `RuntimeWarning: Mean of empty slice.`。
|
||
- **分析**: 当测试集中的所有真实值都为0时,计算MAPE会导致对空数组求平均值。
|
||
- **修复**: 在 `server/analysis/metrics.py` 中增加条件判断,若不存在非零真实值,则直接将MAPE设为0。
|
||
|
||
### 11:41 - 修复“按店铺训练”页面列表加载失败
|
||
- **问题**: “选择店铺”的下拉列表为空。
|
||
- **分析**: `standardize_column_names` 函数错误地移除了包括店铺元数据在内的非训练必需列。
|
||
- **修复**: 将列筛选的逻辑从通用的 `standardize_column_names` 函数中移出,精确地应用到仅为模型训练准备数据的函数中。
|
||
|
||
### 13:00 - 修复“按店铺训练-所有药品”模式
|
||
- **问题**: 选择“所有药品”训练时,因 `product_id` 被错误地处理为字符串 `"unknown"` 而失败。
|
||
- **修复**: 在 `server/core/predictor.py` 中拦截 `"unknown"` ID,并将其意图正确地转换为“聚合此店铺的所有产品数据”。同时扩展了 `aggregate_multi_store_data` 函数,使其支持按店铺ID进行聚合。
|
||
|
||
### 14:19 - 修复并发训练中的稳定性问题
|
||
- **问题**: 并发训练时出现 `API列表排序错误` 和 `WebSocket连接错误`。
|
||
- **修复**:
|
||
1. **排序**: 在 `api.py` 中为 `None` 类型的 `start_time` 提供了默认值,解决了 `TypeError`。
|
||
2. **连接**: 在 `socketio.run()` 调用时增加了 `allow_unsafe_werkzeug=True` 参数,解决了调试模式下Socket.IO与Werkzeug的冲突。
|
||
|
||
### 15:30 - 根治模型训练中的维度不匹配问题
|
||
- **问题**: 所有模型训练完成后,评估指标 `R²` 始终为0.0。
|
||
- **根本原因**: `server/utils/data_utils.py` 的 `create_dataset` 函数在创建目标数据集 `dataY` 时,错误地保留了一个多余的维度。同时,模型文件 (`mlstm_model.py`, `transformer_model.py`) 的输出也存在维度问题。
|
||
- **最终修复**:
|
||
1. **数据层**: 在 `create_dataset` 中使用 `.flatten()` 修正了 `y` 标签的维度。
|
||
2. **模型层**: 在所有模型的 `forward` 方法最后增加了 `.squeeze(-1)`,确保模型输出维度正确。
|
||
3. **训练器层**: 撤销了所有为解决此问题而做的临时性维度调整,恢复了最直接的损失计算。
|
||
|
||
### 16:10 - 修复“全局模型训练-所有药品”模式
|
||
- **问题**: 与“按店铺训练”类似,全局训练的“所有药品”模式也因 `product_id="unknown"` 而失败。
|
||
- **修复**: 采用了与店铺训练完全相同的修复模式。在 `predictor.py` 中拦截 `"unknown"` 并将其意图转换为真正的全局聚合(`product_id=None`),并扩展 `aggregate_multi_store_data` 函数以支持此功能。
|
||
|
||
---
|
||
|
||
## 2025-07-15:端到端修复“按药品预测”图表功能
|
||
**开发者**: lyf
|
||
|
||
### 10:00 - 阶段一:修复数据库写入失败 (`sqlite3.IntegrityError`)
|
||
- **问题**: 后端日志显示 `datatype mismatch`。
|
||
- **分析**: `save_prediction_result` 函数试图将复杂Python对象直接存入数据库。
|
||
- **修复**: 在 `server/api.py` 中,执行数据库插入前,使用 `json.dumps()` 将复杂对象序列化为JSON字符串。
|
||
|
||
### 10:30 - 阶段二:修复API响应结构与前端不匹配
|
||
- **问题**: 图表依然无法渲染。
|
||
- **分析**: 前端期望 `history_data` 在顶层,而后端将其封装在 `data` 子对象中。
|
||
- **修复**: 修改 `server/api.py` 的 `predict` 函数,将关键数据提升到响应的根级别。
|
||
|
||
### 11:00 - 阶段三:修复历史数据与预测数据时间不连续
|
||
- **问题**: 图表数据在时间上完全脱节。
|
||
- **分析**: 获取历史数据的逻辑总是取整个数据集的最后30条,而非预测起始日期之前的30条。
|
||
- **修复**: 在 `server/api.py` 中增加了正确的日期筛选逻辑。
|
||
|
||
### 14:00 - 阶段四:重构数据源,根治数据不一致问题
|
||
- **问题**: 历史数据(绿线)与预测数据(蓝线)的口径完全不同。
|
||
- **根本原因**: API层独立加载**原始数据**画图,而预测器使用**聚合后数据**预测。
|
||
- **修复 (重构)**:
|
||
1. 修改 `server/predictors/model_predictor.py`,使其返回预测结果的同时,也返回其所使用的、口径一致的历史数据。
|
||
2. 彻底删除了 `server/api.py` 中所有独立加载历史数据的冗余代码,确保了数据源的唯一性。
|
||
|
||
### 15:00 - 阶段五:修复图表X轴日期格式问题
|
||
- **问题**: X轴显示为混乱的GMT格式时间戳。
|
||
- **分析**: `history_data` 中的 `Timestamp` 对象未被正确格式化。
|
||
- **修复**: 在 `server/api.py` 中,为 `history_data` 增加了 `.strftime('%Y-%m-%d')` 的格式化处理。
|
||
|
||
### 16:00 - 阶段六:修复模型“学不会”的根本原因 (超参数传递中断)
|
||
- **问题**: 即便流程正确,所有模型的预测结果依然是无法学习的直线。
|
||
- **根本原因**: `server/core/predictor.py` 在调用训练器时,**没有将 `sequence_length` 等关键超参数传递下去**,导致所有模型都使用了错误的默认值。
|
||
- **修复**:
|
||
1. 修改 `server/core/predictor.py`,在调用中加入超参数的传递。
|
||
2. 修改所有四个训练器文件,使其能接收并使用这些参数。
|
||
|
||
---
|
||
|
||
## 2025-07-16:最终验证与项目总结
|
||
**开发者**: lyf
|
||
|
||
### 10:00 - 阶段七:最终验证与结论
|
||
- **问题**: 在修复所有代码问题后,对特定日期的预测结果依然是平线。
|
||
- **分析**: 通过编写临时数据分析脚本 (`temp_check_parquet.py`) 最终确认,这是**数据本身**的问题。我们选择的预测日期在样本数据集中恰好处于一个“零销量”的空白期。
|
||
- **最终结论**: 系统代码已完全修复。图表上显示的平线,是模型对“零销量”历史做出的**正确且符合逻辑**的反应。
|
||
|
||
### 11:45 - 项目总结与文档归档
|
||
- **任务**: 根据用户要求,回顾整个调试过程,将所有问题、解决方案、优化思路和最终结论,按照日期和时间顺序,整理并更新到本开发日志中,形成一份高质量的技术档案。
|
||
- **结果**: 本文档已更新完成。
|
||
|
||
|
||
### 13:15 - 最终修复:根治模型标识符不一致问题
|
||
- **问题**: 经过再次测试和日志分析,发现即便是修正后,店铺模型的 `model_identifier` 在训练时依然被错误地构建为 `01010023_store_01010023`。
|
||
- **根本原因**: `server/core/predictor.py` 的 `train_model` 方法中,在 `training_mode == 'store'` 的分支下,构建 `model_identifier` 的逻辑存在冗余和错误。
|
||
- **最终解决方案**: 删除了错误的拼接逻辑 `model_identifier = f"{store_id}_{product_id}"`,直接使用在之前步骤中已经被正确赋值为 `f"store_{store_id}"` 的 `product_id` 变量作为 `model_identifier`。这确保了从训练、保存到最终API查询,店铺模型的唯一标识符始终保持一致。
|
||
|
||
|
||
### 13:30 - 最终修复(第二轮):根治模型保存路径错误
|
||
- **问题**: 即便修复了标识符,模型版本依然无法加载。
|
||
- **根本原因**: 通过分析训练日志,发现所有训练器(`transformer_trainer.py`, `mlstm_trainer.py`, `tcn_trainer.py`)中的 `save_checkpoint` 函数,都会强制在 `saved_models` 目录下创建一个 `checkpoints` 子目录,并将所有模型文件保存在其中。而负责查找模型的 `get_model_versions` 函数只在根目录查找,导致模型永远无法被发现。
|
||
- **最终解决方案**: 逐一修改了所有相关训练器文件中的 `save_checkpoint` 函数,移除了创建和使用 `checkpoints` 子目录的逻辑,确保所有模型都直接保存在 `saved_models` 根目录下。
|
||
- **结论**: 至此,模型保存的路径与查找的路径完全统一,从根本上解决了模型版本无法加载的问题。
|
||
|
||
|
||
### 13:40 - 最终修复(第三轮):统一所有训练器的模型保存逻辑
|
||
- **问题**: 在修复了 `transformer_trainer.py` 后,发现 `mlstm_trainer.py` 和 `tcn_trainer.py` 存在完全相同的路径和命名错误,导致问题依旧。
|
||
- **根本原因**: `save_checkpoint` 函数在所有训练器中都被错误地实现,它们都强制创建了 `checkpoints` 子目录,并使用了错误的逻辑来拼接文件名。
|
||
- **最终解决方案**:
|
||
1. **逐一修复**: 逐一修改了 `transformer_trainer.py`, `mlstm_trainer.py`, 和 `tcn_trainer.py` 中的 `save_checkpoint` 函数。
|
||
2. **路径修复**: 移除了创建和使用 `checkpoints` 子目录的逻辑,确保模型直接保存在 `model_dir` (即 `saved_models`) 的根目录下。
|
||
3. **文件名修复**: 简化并修正了文件名的生成逻辑,直接使用 `product_id` 参数作为唯一标识符(该参数已由上游逻辑正确赋值为 `药品ID` 或 `store_{店铺ID}`),不再进行任何额外的、错误的拼接。
|
||
- **结论**: 至此,所有训练器的模型保存逻辑完全统一,模型保存的路径和文件名与API的查找逻辑完全匹配,从根本上解决了模型版本无法加载的问题。
|
||
|
||
|
||
---
|
||
|
||
## 2025-07-16 (续):端到端修复“店铺预测”图表功能
|
||
**开发者**: lyf
|
||
|
||
### 15:30 - 最终修复(第四轮):打通店铺预测的数据流
|
||
- **问题**: 在解决了模型加载问题后,“店铺预测”功能虽然可以成功执行,但前端图表依然空白,不显示历史数据和预测数据。
|
||
- **根本原因**: 参数传递在调用链中出现断裂。
|
||
1. `server/api.py` 在调用 `run_prediction` 时,没有传递 `training_mode`。
|
||
2. `server/core/predictor.py` 在调用 `load_model_and_predict` 时,没有传递 `store_id` 和 `training_mode`。
|
||
3. `server/predictors/model_predictor.py` 内部的数据加载逻辑,在处理店铺预测时,错误地使用了模型标识符(`store_{id}`)作为产品ID来过滤数据,导致无法加载到任何历史数据。
|
||
- **最终解决方案 (三步修复)**:
|
||
1. **修复 `model_predictor.py`**: 修改 `load_model_and_predict` 函数,使其能够根据 `training_mode` 参数智能地加载数据。当模式为 `'store'` 时,它会正确地聚合该店铺的所有销售数据作为历史数据,这与训练时的数据准备方式完全一致。
|
||
2. **修复 `predictor.py`**: 修改 `predict` 方法,将 `store_id` 和 `training_mode` 参数正确地传递给底层的 `load_model_and_predict` 函数。
|
||
3. **修复 `api.py`**: 修改 `predict` 路由和 `run_prediction` 辅助函数,确保 `training_mode` 参数在整个调用链中被完整传递。
|
||
- **结论**: 通过以上修复,我们确保了从API接口到最底层数据加载器的参数传递是完整和正确的。现在,无论是药品预测还是店铺预测,系统都能够加载正确的历史数据用于图表绘制,彻底解决了图表显示空白的问题。
|
||
|
||
### 16:16 - 项目状态更新
|
||
- **状态**: **所有已知问题已修复**。
|
||
- **确认**: 用户已确认“现在药品和店铺预测流程通了。
|
||
- **后续**: 将本次修复过程归档至本文档。
|
||
|
||
|
||
---
|
||
|
||
### 2025年7月16日 18:38 - 全模型预测功能通用性修复
|
||
|
||
**问题现象**:
|
||
在解决了 `Transformer` 模型的预测问题后,发现一个更深层次的系统性问题:在所有预测模式(按药品、按店铺、全局)中,只有 `Transformer` 算法可以成功预测并显示图表,而其他四种模型(`mLSTM`, `KAN`, `优化版KAN`, `TCN`)虽然能成功训练,但在预测时均会失败,并提示“没有可用于图表的数据”。
|
||
|
||
**根本原因深度分析**:
|
||
这个问题的核心在于**模型配置的持久化不完整且不统一**。
|
||
|
||
1. **Transformer 的“幸存”**: `Transformer` 模型的实现恰好不依赖于那些在保存时被遗漏的特定超参数,因此它能“幸存”下来。
|
||
2. **其他模型的“共性缺陷”**: 其他所有模型 (`mLSTM`, `TCN`, `KAN`) 在它们的构造函数中,都依赖于一些在训练时定义、但在保存到检查点文件 (`.pth`) 时**被遗漏的**关键结构性参数。
|
||
* **mLSTM**: 缺少 `mlstm_layers`, `embed_dim`, `dense_dim` 等参数。
|
||
* **TCN**: 缺少 `num_channels`, `kernel_size` 等参数。
|
||
* **KAN**: 缺少 `hidden_sizes` 列表。
|
||
3. **连锁失败**:
|
||
* 当 `server/predictors/model_predictor.py` 尝试加载这些模型的检查点文件时,它从 `checkpoint['config']` 中找不到实例化模型所必需的全部参数。
|
||
* 模型实例化失败,抛出 `KeyError` 或 `TypeError`。
|
||
* 这个异常导致 `load_model_and_predict` 函数提前返回 `None`,最终导致返回给前端的响应中缺少 `history_data`,前端因此无法渲染图表。
|
||
|
||
**系统性、可扩展的解决方案**:
|
||
为了彻底解决这个问题,并为未来平稳地加入新算法,我们对所有非 Transformer 的训练器进行了标准化的、彻底的修复。
|
||
|
||
1. **修复 `mlstm_trainer.py`**: 在 `config` 字典中补全了 `mlstm_layers`, `embed_dim`, `dense_dim` 等所有缺失的参数。
|
||
2. **修复 `tcn_trainer.py`**: 在 `config` 字典中补全了 `num_channels`, `kernel_size` 等所有缺失的参数。
|
||
3. **修复 `kan_trainer.py`**: 在 `config` 字典中补全了 `hidden_sizes` 列表。
|
||
|
||
**结果**:
|
||
通过这次系统性的修复,我们确保了所有训练器在保存模型时,都会将完整的、可用于重新实例化模型的配置信息写入检查点文件。这从根本上解决了所有模型算法的预测失败问题,使得整个系统在处理不同算法时具有了通用性和健壮性。
|
||
|
||
|
||
---
|
||
|
||
## 2025-07-17:系统性链路疏通与规范化
|
||
**开发者**: lyf
|
||
|
||
### 15:00 - 创建技术文档与上手指南
|
||
- **任务**: 为了便于新成员理解和未来维护,创建了两份核心技术文档。
|
||
- **产出**:
|
||
1. **`系统调用逻辑与核心代码分析.md`**: 一份深入代码细节的端到端调用链路分析文档,详细描述了从前端交互到后端处理,再到模型训练和预测的完整流程。
|
||
2. **`项目快速上手指南.md`**: 一份面向新成员(特别是Java背景)的高层次指南,通过技术栈类比、架构分层图和清晰的开发流程,帮助新成员快速建立对项目的宏观理解。
|
||
|
||
### 16:00 - 修复 `mLSTM` 模型加载链路
|
||
- **问题**: `mLSTM` 模型在预测时因参数名不一致而加载失败。
|
||
- **分析**:
|
||
- 第一次失败: 加载器需要 `num_layers`,但训练器保存的是 `mlstm_layers`。
|
||
- 第二次失败: 加载器需要 `dropout`,但训练器保存的是 `dropout_rate`。
|
||
- **修复**: 遵循“保存方决定命名”的原则,修改了 `server/predictors/model_predictor.py`,将加载时使用的参数名统一为 `mlstm_layers` 和 `dropout_rate`,与训练器保持一致。
|
||
|
||
### 16:45 - 修复 `mLSTM` 模型算法缺陷
|
||
- **问题**: `mLSTM` 模型修复加载问题后,预测结果为一条无效的直线。
|
||
- **根本原因**: `server/models/mlstm_model.py` 中的模型架构存在设计缺陷。其解码器逻辑错误地将输入序列的最后一个时间步复制多份作为预测,导致模型无法学习时间序列的变化。
|
||
- **修复**: 重构了 `MLSTMTransformer` 类的 `forward` 方法,移除了有问题的解码器逻辑,改为直接使用编码器最终的隐藏状态通过一个线性层进行预测,从根本上修正了算法的实现。
|
||
|
||
### 17:00 - 修复 `TCN` 模型加载链路
|
||
- **问题**: `TCN` 模型在预测加载时存在硬编码参数,是一个潜在的崩溃点。
|
||
- **分析**: `server/predictors/model_predictor.py` 在创建 `TCNForecaster` 实例时,硬编码了 `kernel_size=3`,而没有从模型配置中读取。
|
||
- **修复**: 修改了 `model_predictor.py`,使其从 `config['kernel_size']` 中动态读取该参数,确保了配置的完整性和一致性。
|
||
|
||
### 17:15 - 修复 `KAN` 模型版本发现问题
|
||
- **问题**: `KAN` 和 `优化版KAN` 训练成功后,在预测页面无法找到任何模型版本。
|
||
- **根本原因**: **保存**和**搜索**逻辑不匹配。`kan_trainer.py` 使用 `model_manager.py` 以 `..._product_...` 格式保存模型,而 `server/core/config.py` 中的 `get_model_versions` 函数却只按 `..._epoch_...` 的格式进行搜索。
|
||
- **修复**: 扩展了 `config.py` 中的 `get_model_versions` 函数,使其能够兼容并搜索多种命名格式,包括 `KAN` 模型使用的 `..._product_...` 格式。
|
||
|
||
### 17:25 - 修复 `KAN` 模型文件路径生成问题
|
||
- **问题**: 修复版本发现问题后,点击预测依然失败,提示“未找到模型文件”。
|
||
- **根本原因**: 只修复了**版本发现**逻辑,但未同步修复**文件路径生成**逻辑。`config.py` 中的 `get_model_file_path` 函数在为 `KAN` 模型生成路径时,依然错误地使用了 `_epoch_` 格式。
|
||
- **修复**: 修改了 `get_model_file_path` 函数,为 `kan` 和 `optimized_kan` 模型增加了特殊处理,确保在生成其文件路径时使用正确的 `_product_` 命名格式。
|
||
|
||
### 17:40 - 升级 `KAN` 训练器的版本管理功能
|
||
- **问题**: `KAN` 模型只有一个静态的 `'v1'` 版本,与其他模型(有 `best`, `final_epoch_...` 等版本)不一致。
|
||
- **根本原因**: `kan_trainer.py` 的实现逻辑过于简单,缺少在训练过程中动态评估并保存多个版本的功能,仅在最后硬编码保存为 `'v1'`。
|
||
- **修复 (功能升级)**: 重构了 `server/trainers/kan_trainer.py`,为其增加了与其他训练器完全一致的动态版本管理功能。现在它可以在训练时自动追踪并保存性能最佳的 `best` 版本,并在训练结束后保存 `final_epoch_...` 版本。
|
||
|
||
### 17:58 - 最终结论
|
||
- **状态**: **所有已知问题已修复**。
|
||
- **成果**:
|
||
1. 所有模型的 **“数据 -> 训练 -> 保存 -> 加载 -> 预测 -> 可视化”** 执行链路已全面打通和验证。
|
||
2. 统一并修复了所有模型在配置持久化和加载过程中的参数不一致问题。
|
||
3. 将所有模型的版本管理逻辑和工程实现标准完全对齐。
|
||
4. 创建并完善了核心技术文档,固化了开发规范。
|
||
- **项目状态**: 系统现在处于一个健壮、一致且可扩展的稳定状态。
|
||
|
||
---
|
||
|
||
## 2025-07-18: 系统性重构模型版本管理机制
|
||
**开发者**: lyf
|
||
|
||
### 14:00 - 根治版本混乱与模型加载失败问题
|
||
- **问题现象**: `KAN` 及其他算法在训练后,预测时出现版本号混乱(如出现裸数字 `1`、`3` 或 `best` 等无效版本)、版本重复、以及因版本不匹配导致的“模型文件未找到”的 `404` 错误。
|
||
- **根本原因深度分析**:
|
||
1. **逻辑分散**: 版本生成的逻辑分散在各个训练器 (`trainer`) 中,而版本发现的逻辑在 `config.py` 中,两者标准不一,充满冲突的正则表达式和硬编码规则。
|
||
2. **命名不统一**: `KAN` 训练器使用 `model_manager` 保存,而其他训练器使用本地的 `save_checkpoint` 函数,导致了 `..._product_..._v1.pth` 和 `..._epoch_best.pth` 等多种不兼容的命名格式并存。
|
||
3. **提取错误**: `config.py` 中的 `get_model_versions` 函数因其过于宽泛和冲突的匹配规则,会从文件名中错误地提取出无效的版本号,是导致前端下拉框内容混乱的直接原因。
|
||
- **系统性重构解决方案**:
|
||
1. **确立单一权威**: 将 [`server/utils/model_manager.py`](server/utils/model_manager.py:1) 确立为系统中唯一负责版本管理、模型命名和文件IO的组件。
|
||
2. **实现自动版本控制**: 在 `ModelManager` 中增加了 `_get_next_version` 内部方法,使其能够自动扫描现有文件,并安全地生成下一个递增的、带 `v` 前缀的版本号(如 `v3`)。
|
||
3. **统一所有训练器**: 全面重构了 `kan_trainer.py`, `mlstm_trainer.py`, `tcn_trainer.py`, 和 `transformer_trainer.py`。现在,所有训练器在保存最终模型时,都调用 `model_manager.save_model` 并且**不再自行决定版本号**,完全由 `ModelManager` 自动生成。对于训练过程中的最佳模型,则统一显式保存为 `best` 版本。
|
||
4. **清理与加固**: 废弃并删除了 `config.py` 中所有旧的、有问题的版本管理函数,并重写了 `get_model_versions`,使其只使用严格的正则表达式来查找和解析符合新命名规范的模型版本。
|
||
5. **优化API**: 更新了 `api.py`,使其完全与新的 `ModelManager` 对接,并改进了预测失败时的错误信息反馈。
|
||
- **结论**: 通过这次重构,系统的版本管理机制从一个分散、混乱、充满硬编码的状态,升级为了一个集中的、统一的、自动化的健壮系统。所有已知相关的bug已被从根本上解决。
|
||
|
||
---
|
||
|
||
## 2025-07-18 (续): 实现“按店铺”AI闭环及连锁Bug修复
|
||
**开发者**: lyf
|
||
|
||
### 15:00 - 架构升级:实现“按店铺”训练与预测功能
|
||
- **任务目标**: 在现有“按药品”模式基础上,增加并打通“按店铺”维度的完整AI闭环。
|
||
- **核心挑战**: 需要对数据处理、模型标识、训练流程和API调用进行系统性改造,以支持新的训练模式。
|
||
- **解决方案 (四步重构)**:
|
||
1. **升级 `ModelManager`**: 重新设计了模型命名规则,为店铺和全局模型提供了清晰、无歧义的标识(如 `transformer_store_S001_v1.pth`),并同步更新了解析逻辑。
|
||
2. **修正核心预测器**: 修复了 `predictor.py` 中的关键逻辑缺陷,确保在店铺模式下,系统能生成并使用正确的 `model_identifier`(如 `store_S001`),并强制调用数据聚合函数。
|
||
3. **适配API层**: 调整了 `api.py` 中的训练和预测接口,使其能够兼容和正确处理新的店铺模式请求。
|
||
4. **统一所有训练器**: 对全部四个训练器文件进行了统一修改,确保它们在保存模型时,都正确地使用了新的 `model_identifier`。
|
||
|
||
### 15:30 - 连锁Bug修复第一环:解决店铺模型版本加载失败
|
||
- **问题现象**: “按店铺预测”页面的模型版本下拉框为空。
|
||
- **根本原因**: `api.py` 中负责获取店铺模型版本的接口 `get_store_model_versions_api` 仍在使用旧的、不兼容新命名规范的函数来查找模型。
|
||
- **修复**: 重写了该接口,使其放弃旧函数,转而使用 `ModelManager` 来进行统一、可靠的模型查找。
|
||
|
||
### 15:40 - 连锁Bug修复第二环:解决店铺预测 `404` 失败
|
||
- **问题现象**: 版本列表加载正常后,点击“开始预测”返回 `404` 错误。
|
||
- **根本原因**: 后端预测接口 `predict()` 内部的执行函数 `load_model_and_predict` 存在一段过时的、手动的模型文件查找逻辑,它完全绕过了 `ModelManager`,并错误地构建了文件路径。
|
||
- **修复 (联合重构)**:
|
||
1. **改造 `model_predictor.py`**: 彻底移除了 `load_model_and_predict` 函数内部所有过时的文件查找代码,并修改其函数签名,使其直接接收一个明确的 `model_path` 参数。
|
||
2. **改造 `api.py`**: 修改了 `predict` 接口,将在API层通过 `ModelManager` 找到的正确模型路径,一路传递到最底层的 `load_model_and_predict` 函数中,确保了调用链的逻辑一致性。
|
||
|
||
### 15:50 - 连锁Bug修复第三环:解决服务启动 `NameError`
|
||
- **问题现象**: 在修复预测逻辑后,API服务无法启动,报错 `NameError: name 'Optional' is not defined`。
|
||
- **根本原因**: 在修改 `model_predictor.py` 时,使用了 `Optional` 类型提示,但忘记从 `typing` 模块导入。
|
||
- **修复**: 在 `server/predictors/model_predictor.py` 文件顶部添加了 `from typing import Optional`。
|
||
- **最终结论**: 至此,所有与“按店铺”功能相关的架构升级和连锁bug均已修复。系统现在能够稳定、正确地处理两种维度的训练和预测任务,并且代码逻辑更加统一和健壮。
|
||
|
||
|
||
---
|
||
|
||
## 2025-07-21:前后端联合调试与UI修复
|
||
**开发者**: lyf
|
||
|
||
### 15:45 - 修复后端 `DataFrame` 序列化错误
|
||
- **问题现象**: 在清理了历史模型并重新进行预测后,前端出现 `Object of type DataFrame is not JSON serializable` 错误。
|
||
- **根本原因**: `server/predictors/model_predictor.py` 中的 `load_model_and_predict` 函数在返回结果时,为了兼容旧版接口而保留的 `'predictions'` 字段,其值依然是未经处理的 Pandas DataFrame (`predictions_df`)。
|
||
- **修复方案**: 修改了该函数的返回字典,将 `'predictions'` 字段的值也更新为已经过 `.to_dict('records')` 方法处理的 `prediction_data_json`,确保了返回对象的所有部分都是JSON兼容的。
|
||
|
||
### 16:00 - 统一修复所有预测视图的图表渲染问题
|
||
- **问题现象**: 在解决了后端的序列化问题后,所有三个预测视图(按药品、按店铺、全局)的图表均为空白,并且图表下方的日期副标题显示为未经格式化的原始JavaScript日期字符串。
|
||
- **根本原因深度分析**:
|
||
1. **数据访问路径不精确**: 前端代码直接从API响应的根对象 (`response.data`) 中获取数据,而最可靠的数据源位于 `response.data.data` 中。
|
||
2. **日期对象处理不当**: 前端代码未能将从后端接收到的日期(无论是字符串还是由axios自动转换的Date对象)标准化为统一的字符串格式。这导致在使用 `Set` 对日期进行去重时,因对象引用不同而失败,最终图表上没有数据点。
|
||
- **统一修复方案**:
|
||
1. **逐一修改**: 逐一修改了 `ProductPredictionView.vue`, `StorePredictionView.vue`, 和 `GlobalPredictionView.vue` 三个文件。
|
||
2. **修正数据访问**: 在 `startPrediction` 方法中,将API响应的核心数据 `response.data.data` 赋值给 `predictionResult`。
|
||
3. **标准化日期**: 在 `renderChart` 方法的开头,增加了一个 `formatDate` 辅助函数,并在处理数据时立即调用它,将所有日期都统一转换为 `'YYYY-MM-DD'` 格式的字符串,从而一举解决了数据点丢失和标题格式错误的双重问题。
|
||
- **最终结论**: 至此,所有预测视图的前后端数据链路和UI展示功能均已修复,系统功能恢复正常。
|
||
|
||
|
||
---
|
||
|
||
## 2025-07-22:UI导航与Git流程优化
|
||
**开发者**: lyf
|
||
|
||
### 11:00 - 修复“历史预测”页面导航失效问题
|
||
- **问题现象**: 从“历史预测”页面 (`/history`) 导航到任何其他页面时,浏览器地址栏的URL会更新,但视图内容保持不变,必须手动刷新页面才能正确跳转。
|
||
- **根本原因**: `UI/src/views/HistoryView.vue` 组件的 `onUnmounted` 生命周期钩子函数中,代码尝试清理两个图表实例 `fullscreenPredictionChart` 和 `fullscreenHistoryChart`。然而,这两个变量从未在组件中被定义,导致在离开页面时触发 `ReferenceError`。这个JavaScript运行时错误中断了Vue Router的后续导航处理,导致跳转失败。
|
||
- **修复方案**: 修改了 `HistoryView.vue` 文件中的 `onUnmounted` 钩子和 `resizeCharts` 函数,将对未定义变量的引用修正为组件中实际使用的、正确的图表实例变量 `predictionChart` 和 `historyChart`,从根源上解决了该运行时错误。
|