Compare commits

..

3 Commits

6 changed files with 161 additions and 102 deletions

View File

@ -374,3 +374,24 @@
1. **后端代码修复**: 修改了 `server/api.py``predict` 函数,移除了默认值,强制要求前端在请求中必须提供 `future_days``history_lookback_days` 参数,确保用户的设置能被正确处理。
2. **历史数据修复**: 创建并执行了一个新的Python脚本 `fix_old_predictions.py`。该脚本遍历数据库中所有已存在的历史记录识别出被截断的数据并使用原始参数重新生成完整的预测结果覆盖掉旧的、不完整的数据文件。该脚本也经过了多次调试以处理文件编码、方法调用错误和JSON序列化等问题。
- **最终结论**: 至此,所有与“历史预测”模块相关的功能缺陷和数据一致性问题均已得到彻底解决。系统现在能够正确生成、保存、修复并完整展示所有历史预测的结果。
---
## 2025-07-25项目文档体系建立与路径Bug修复
**开发者**: Roo (AI Assistant) & lyf
### 18:30 - 修复预测结果保存路径错误
- **问题现象**: 预测成功后,生成的详细结果 `.json` 文件被错误地保存到了 `static/predictions/` 目录下,而非预期的 `saved_predictions/` 目录。
- **根本原因**: `server/api.py` 中的 `save_prediction_result` 辅助函数硬编码了旧的、不规范的保存路径。
- **修复方案**:
1. **标准化配置**: 在 `server/core/config.py` 中新增了 `DEFAULT_PREDICTIONS_DIR` 配置项,指向正确的 `saved_predictions/` 目录。
2. **修正代码**: 修改了 `server/api.py` 中的 `save_prediction_result` 函数,使其从配置文件中读取正确的路径,彻底解决了硬编码问题。
### 18:45 - 创建并完善项目核心技术文档
- **任务目标**: 解决项目因快速迭代而导致的文档缺失与过时问题,为新成员提供准确的上手材料,并固化当前稳定的系统架构。
- **实施过程**:
1. **全面分析**: 对项目的技术栈、核心工作流、数据存储结构、异步任务处理和模块化设计(如模型管理器、注册表)进行了全面的代码级分析。
2. **撰写新指南**: 基于分析结果,撰写并覆盖生成了一份全新的、内容详尽的 **`项目快速上手指南.md`**。
3. **文档迭代**: 根据开发者的提问,在新指南中补充了关于“数据库索引 -> JSON文件内容”的读取机制说明以及关于如何管理和清理历史产物的“系统维护与扩展”章节。
- **最终成果**: 产出了一份高质量、与当前代码完全同步的核心技术指南,显著提升了项目的可维护性和知识传承效率。

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@ -57,6 +57,7 @@ from analysis.metrics import evaluate_model, compare_models
# 导入配置和版本管理
from core.config import (
DEFAULT_MODEL_DIR, WEBSOCKET_NAMESPACE,
DEFAULT_PREDICTIONS_DIR,
get_model_versions,
get_model_file_path, save_model_version_info
)
@ -3093,8 +3094,8 @@ def save_prediction_result(prediction_result, product_id, product_name, model_ty
# 生成唯一的预测ID
prediction_id = str(uuid.uuid4())
# 确保目录存在
os.makedirs('static/predictions', exist_ok=True)
# 确保目录存在 (使用配置项)
os.makedirs(DEFAULT_PREDICTIONS_DIR, exist_ok=True)
# 处理预测结果中可能存在的NumPy类型
@ -3119,9 +3120,9 @@ def save_prediction_result(prediction_result, product_id, product_name, model_ty
# 转换整个预测结果对象
prediction_result = convert_numpy_types(prediction_result)
# 保存预测结果到JSON文件
# 保存预测结果到JSON文件 (使用配置项)
file_name = f"prediction_{prediction_id}.json"
file_path = os.path.join('static/predictions', file_name)
file_path = os.path.join(DEFAULT_PREDICTIONS_DIR, file_name)
with open(file_path, 'w', encoding='utf-8') as f:
json.dump(prediction_result, f, ensure_ascii=False, cls=CustomJSONEncoder)

View File

@ -36,6 +36,7 @@ DEVICE = get_device()
# 使用 os.path.join 构造跨平台的路径
DEFAULT_DATA_PATH = os.path.join(PROJECT_ROOT, 'data', 'timeseries_training_data_sample_10s50p.parquet')
DEFAULT_MODEL_DIR = os.path.join(PROJECT_ROOT, 'saved_models')
DEFAULT_PREDICTIONS_DIR = os.path.join(PROJECT_ROOT, 'saved_predictions')
DEFAULT_FEATURES = ['sales', 'price', 'weekday', 'month', 'is_holiday', 'is_weekend', 'is_promotion', 'temperature']
# 时间序列参数
@ -70,8 +71,9 @@ DEFAULT_VERSION = 'v1' # 默认版本号
WEBSOCKET_NAMESPACE = '/training' # WebSocket命名空间
TRAINING_UPDATE_INTERVAL = 1 # 训练进度更新间隔(秒)
# 创建模型保存目录
# 创建模型和预测结果保存目录
os.makedirs(DEFAULT_MODEL_DIR, exist_ok=True)
os.makedirs(DEFAULT_PREDICTIONS_DIR, exist_ok=True)
def get_model_file_path(product_id: str, model_type: str, version: str) -> str:

View File

@ -1,127 +1,161 @@
# 项目快速上手指南 (面向新开发者)
# 项目快速上手指南 (v2.0 - 2025年7月版)
欢迎加入项目!本指南旨在帮助你快速理解项目的核心功能、技术架构和开发流程特别是为你一位Java背景的开发者提供清晰的切入点
欢迎加入项目!本指南旨在帮助你快速理解项目的核心功能、最新的技术架构和开发流程。
## 1. 项目是做什么的?(实现了什么功能)
## 1. 项目核心功能
这是一个基于历史销售数据的 **智能销售预测系统**
这是一个基于历史销售数据的 **智能销售预测系统**,其核心是实现一个 **"数据 -> 训练 -> 模型 -> 预测 -> 可视化"** 的完整闭环
核心功能有三个,全部通过Web界面操作
1. **模型训练**: 用户可以选择某个**药品**、某个**店铺**或**全局**数据然后选择一种机器学习算法如Transformer、mLSTM等进行训练最终生成一个预测模型
所有功能均通过Web界面操作
1. **模型训练**: 用户可以选择某个**药品**、某个**店铺**或**全局**数据然后选择一种机器学习算法如Transformer、KAN等进行训练。训练过程是**异步**的并能通过WebSocket实时反馈进度
2. **销售预测**: 使用已经训练好的模型,对未来的销量进行预测。
3. **结果可视化**: 将历史销量和预测销量在同一个图表中展示出来,方便用户直观地看到趋势。
4. **模型与历史管理**: 提供对已训练模型和历史预测记录的查询、详情查看和删除功能。
简单来说,它就是一个 **"数据 -> 训练 -> 模型 -> 预测 -> 可视化"** 的完整闭环应用。
## 2. 技术栈
## 2. 用了什么技术?(技术栈)
| 层面 | 本项目技术 | 说明 |
| :--- | :--- | :--- |
| **后端框架** | **Flask** | 轻量级的Python Web框架用于提供API接口。 |
| **前端框架** | **Vue.js** | 用于构建用户交互界面的现代化JavaScript框架。 |
| **核心算法库** | **PyTorch** | 实现深度学习算法的核心库。 |
| **数据处理** | **Pandas** | Python中用于数据分析和处理的核心库。 |
| **数据库** | **SQLite** | 一个轻量级的本地文件数据库,用于记录模型元数据和预测历史。 |
| **实时通信** | **Flask-SocketIO** | 用于后端在训练时向前端实时推送日志和进度。 |
| **异步任务** | **multiprocessing** | Python标准库用于将耗时的训练任务放到独立的子进程中执行避免阻塞API服务。 |
你可以将这个项目的技术栈与Java世界进行类比
## 3. 系统架构与数据流
| 层面 | 本项目技术 | Java世界类比 | 说明 |
| :--- | :--- | :--- | :--- |
| **后端框架** | **Flask** | Spring Boot | 一个轻量级的Web框架用于提供API接口。 |
| **前端框架** | **Vue.js** | React / Angular | 用于构建用户交互界面的现代化JavaScript框架。 |
| **核心算法库** | **PyTorch** | (无直接对应) | 类似于Java的Deeplearning4j是实现深度学习算法的核心。 |
| **数据处理** | **Pandas** | (无直接对应) | Python中用于数据分析和处理的“瑞士军刀”可以看作是内存中的强大数据表格。 |
| **构建/打包** | **Vite** (前端) | Maven / Gradle | 前端项目的构建和依赖管理工具。 |
| **数据库** | **SQLite** | H2 / MySQL | 一个轻量级的本地文件数据库,用于记录预测历史等。 |
| **实时通信** | **Socket.IO** | WebSocket / STOMP | 用于后端在训练时向前端实时推送进度。 |
## 3. 系统架构是怎样的?(架构层级和设计)
本项目是经典的前后端分离架构,可以分为四个主要层次:
本项目是经典的前后端分离架构,其数据流和核心组件如下:
```
+------------------------------------------------------+
| 用户 (Browser) |
+------------------------------------------------------+
|
+------------------------------------------------------+
| 1. 前端层 (Frontend - Vue.js) |
| - Views (页面组件, e.g., ProductPredictionView.vue) |
| - API Calls (使用axios与后端通信) |
| - Charting (使用Chart.js进行图表渲染) |
+------------------------------------------------------+
| (HTTP/S, WebSocket)
+------------------------------------------------------+
| 2. 后端API层 (Backend API - Flask) |
| - api.py (类似Controller, 定义RESTful接口) |
| - 接收请求, 验证参数, 调用业务逻辑层 |
+------------------------------------------------------+
|
+------------------------------------------------------+
| 3. 业务逻辑层 (Business Logic - Python) |
| - core/predictor.py (类似Service层) |
| - 封装核心业务, 如“根据参数选择合适的训练器” |
+------------------------------------------------------+
|
+------------------------------------------------------+
| 4. 数据与模型层 (Data & Model - PyTorch/Pandas) |
| - trainers/*.py (具体的算法实现和训练逻辑) |
| - predictors/model_predictor.py (模型加载与预测逻辑) |
| - saved_models/ (存放训练好的.pth模型文件) |
| - data/ (存放原始数据.parquet文件) |
+------------------------------------------------------+
+-----------------------------------------------------------------+
| 用户 (Browser - Vue.js) |
+-----------------------------------------------------------------+
| (1. HTTP/WebSocket请求)
+-----------------------------------------------------------------+
| 后端API层 (Backend API - Flask) |
| - api.py: 定义所有RESTful接口 (e.g., /api/training) |
| - 接收请求, 验证参数, 调用核心服务层 |
+-----------------------------------------------------------------+
| (2. 调用核心服务)
+-----------------------------------------------------------------+
| 核心服务与管理层 (Core Services) |
| - training_process_manager.py: 异步训练任务管理器 (关键) |
| - model_manager.py: 模型保存、加载、版本控制 (关键) |
| - model_registry.py: 算法与训练器的注册表 (关键) |
+-----------------------------------------------------------------+
| (3. 执行具体任务)
+-----------------------------------------------------------------+
| 算法实现与数据处理层 (Algorithm & Data) |
| - trainers/*.py: 具体的算法训练逻辑 (e.g., kan_trainer.py) |
| - predictors/model_predictor.py: 模型加载与预测逻辑 |
| - models/*.py: PyTorch模型定义 (e.g., kan_model.py) |
| - utils/data_utils.py: 数据预处理和转换工具 |
+-----------------------------------------------------------------+
| (4. 读写物理文件)
+-----------------------------------------------------------------+
| 物理存储层 (Storage) |
| - data/*.parquet: 原始时序数据 |
| - saved_models/*.pth: 训练好的模型文件 (权重、配置、缩放器) |
| - saved_predictions/*.json: 详细的预测结果文件 |
| - prediction_history.db: SQLite数据库 (存储元数据) |
+-----------------------------------------------------------------+
```
## 4. 关键执行流程
## 4. 核心工作流详解:“数据 -> 训练 -> 预测”
以最常见的“按药品预测”为例:
#### **步骤一:数据准备**
- **原始数据**: 存储在 [`data/timeseries_training_data_sample_10s50p.parquet`](data/timeseries_training_data_sample_10s50p.parquet)。
- **元数据**: 存储在根目录的 `prediction_history.db` SQLite数据库中。
1. **前端**: 用户在页面上选择药品和模型点击“预测”按钮。Vue组件通过`axios`向后端发送一个POST请求到 `/api/prediction`
2. **API层**: `api.py` 接收到请求像一个Controller一样解析出药品ID、模型类型等参数。
3. **业务逻辑层**: `api.py` 调用 `core/predictor.py` 中的 `predict` 方法,将参数传递下去。这一层是业务的“调度中心”。
4. **模型层**: `core/predictor.py` 最终调用 `predictors/model_predictor.py` 中的 `load_model_and_predict` 函数。
5. **模型加载与执行**:
* 根据参数在 `saved_models/` 目录下找到对应的模型文件(例如 `transformer_store_01010023_best.pth``mlstm_product_17002608_v3.pth`)。
* 加载文件,从中恢复出 **模型结构**、**模型权重** 和 **数据缩放器**
* 准备最新的历史数据作为输入,执行预测。
* 将预测结果返回。
6. **返回与渲染**: 结果逐层返回到`api.py`在这里被格式化为JSON然后发送给前端。前端接收到JSON后使用`Chart.js`将历史和预测数据画在图表上。
#### **步骤二:模型训练 (异步)**
1. **API触发**: 前端调用 `POST /api/training` ([`server/api.py`](server/api.py:815))。
2. **任务提交**: `api.py` 将训练请求提交给**训练进程管理器** (`training_process_manager`)并立即返回一个任务ID不阻塞主服务。
3. **动态执行**:
* 管理器在**新的子进程**中运行任务。
* 它通过**模型注册表** (`model_registry`) 找到 `model_type` 对应的训练器函数(例如,`kan` -> `kan_trainer.py` 中的函数)。
* 训练器加载数据进行归一化然后执行PyTorch训练循环。
4. **模型保存**:
* 训练完成后,调用**模型管理器** (`model_manager`) 的 `save_model` 方法。
* 模型(权重、配置、缩放器)被打包保存在 [`saved_models/`](saved_models/) 目录下,命名如 `kan_product_P001_v1.pth`
* 模型的元数据(路径、版本、指标)被写入数据库的 `model_versions` 表。
#### **步骤三:模型预测 (同步)**
1. **API触发**: 前端调用 `POST /api/prediction` ([`server/api.py`](server/api.py:1273))。
2. **模型定位**: `api.py` 调用**模型管理器** (`model_manager`),根据参数从数据库中找到对应的模型文件路径。
3. **加载与预测**:
* 核心逻辑在 [`server/predictors/model_predictor.py`](server/predictors/model_predictor.py) 的 `load_model_and_predict` 函数中。
* 该函数加载 `.pth` 文件,并利用其中的 `config` 和 `state_dict` **精确重建模型实例**。
* 执行**自回归预测**:预测一天,将结果作为下一天输入的一部分,循环往复。
4. **结果保存**:
* 完整的预测结果历史、预测、分析等被保存为一个JSON文件到 [`saved_predictions/`](saved_predictions/) 目录。
* 该次预测的元数据包括JSON文件路径被写入数据库的 `prediction_history` 表。
5. **返回与渲染**: 完整的JSON结果被返回给前端前端使用图表库进行可视化。
## 5. 如何添加一个新的算法?(开发者指南)
这是你最可能接触到的新功能开发。假设你要添加一个名为 `NewNet` 的新算法,你需要按以下步骤操作:
假设你要添加一个名为 `NewNet` 的新算法
**目标**: 让 `NewNet` 出现在前端的“模型类型”下拉框中,并能成功训练和预测。
1. **创建训练器文件**:
* 在 `server/trainers/` 目录下,复制一份现有的训练器文件(例如 `tcn_trainer.py`)并重命名为 `newnet_trainer.py`
* 在 `newnet_trainer.py` 中:
* 定义你的 `NewNet` 模型类(继承自 `torch.nn.Module`)。
* 修改 `train_..._with_tcn` 函数,将其重命名为 `train_..._with_newnet`
* 在这个新函数里,确保实例化的是你的 `NewNet` 模型。
* **最关键的一步**: 在保存checkpoint时确保 `config` 字典里包含了重建 `NewNet` 所需的所有超参数(比如层数、节点数等)。
1. **创建模型定义文件**:
* 在 `server/models/` 目录下,创建 `newnet_model.py`
* 在其中定义你的 `NewNet` 模型类,继承自 `torch.nn.Module`
* **重要开发规范:参数命名规则**
为了防止在模型加载时出现参数不匹配的错误(例如 `KeyError: 'num_layers'`),我们制定了以下命名规范:
> **规则:** 对于特定于某个算法的超参数,其在 `config` 字典中的键名key必须以该算法的名称作为前缀或唯一标识。
2. **创建训练器文件**:
* 在 `server/trainers/` 目录下,创建 `newnet_trainer.py`
* 复制一份现有训练器(如 `kan_trainer.py`)的内容作为模板。
* **关键修改**:
* 导入你的 `NewNet` 模型。
* 在训练函数中,实例化你的 `NewNet` 模型。
* 在保存checkpoint时确保 `config` 字典里包含了重建 `NewNet` 所需的所有超参数。
* 在文件末尾,**注册你的训练器**: `register_trainer('newnet', your_training_function)`
**示例:**
* 对于 `mLSTM` 模型的层数,键名应为 `mlstm_layers`
* 对于 `TCN` 模型的通道数,键名可以是 `tcn_channels`
* 对于 `Transformer` 模型的编码器层数,键名可以是 `num_encoder_layers` 因为这在Transformer语境下是明确的
3. **创建预测器逻辑 (如果需要)**:
* 大多数情况,你可以复用默认的预测器。打开 [`server/predictors/model_predictor.py`](server/predictors/model_predictor.py)
* 在 `load_model_and_predict` 函数中,添加一个 `elif loaded_model_type == 'newnet':` 分支,确保它能根据 `config` 正确地创建 `NewNet` 模型实例
* 在文件末尾,**注册你的预测器**: `register_predictor('newnet', default_pytorch_predictor)`。如果你的模型有特殊的预测逻辑,可以自定义一个预测函数并注册它
**加载模型时** ([`server/predictors/model_predictor.py`](server/predictors/model_predictor.py:1)),必须使用与保存时完全一致的键名来读取这些参数。遵循此规则可以从根本上杜绝因参数名不一致导致的模型加载失败问题。
4. **更新前端界面**:
* 打开 `server/api.py` 中的 `get_model_types` 函数。
* 在 `model_meta` 字典中添加 `'newnet'` 的元数据,包括名称、描述和标签类型。
* **无需修改前端代码**前端的下拉框会自动从这个API获取最新的模型列表。
2. **注册新模型**:
* 打开 `server/core/config.py` 文件。
* 找到 `SUPPORTED_MODELS` 列表。
* 在列表中添加你的新模型标识符 `'newnet'`
完成以上步骤后,重启服务,你就可以在界面上选择并使用你的新算法了。这个插件式的设计大大简化了新算法的集成过程。
3. **接入业务逻辑层 (训练)**:
* 打开 `server/core/predictor.py` 文件。
* 在 `train_model` 方法中,找到 `if/elif` 模型选择逻辑。
* 添加一个新的 `elif model_type == 'newnet':` 分支,让它调用你在第一步中创建的 `train_..._with_newnet` 函数。
## 6. 系统维护与扩展
4. **接入模型层 (预测)**:
* 打开 `server/predictors/model_predictor.py` 文件。
* 在 `load_model_and_predict` 函数中,找到 `if/elif` 模型实例化逻辑。
* 添加一个新的 `elif model_type == 'newnet':` 分支,确保它能根据 `config` 正确地创建 `NewNet` 模型实例。
随着系统的持续运行,会不断产生模型文件、预测结果等历史产物。理解如何管理这些产物对于保持系统的健康至关重要。
5. **更新前端界面**:
* 打开 `UI/src/views/training/``UI/src/views/prediction/` 目录下的相关Vue文件`ProductTrainingView.vue`)。
* 找到定义模型选项的地方(通常是一个数组或对象)。
* 添加 `{ label: '新网络模型 (NewNet)', value: 'newnet' }` 这样的新选项。
### 6.1. 历史产物管理 (Artifacts Management)
完成以上步骤后,重启服务,你就可以在界面上选择并使用你的新算法了。
**问题**:
随着时间推移,`saved_models/``saved_predictions/` 目录下的文件会越来越多,导致项目体积变得臃肿。
**当前状态**:
系统目前依赖**手动清理**。您可以通过Web界面删除单个模型或单条预测历史程序会自动删除对应的文件。
**推荐的解决方案**:
为了实现自动化管理,推荐创建一个独立的维护脚本,并实施**数据保留策略 (Data Retention Policy)**。
#### **自动化清理策略**
1. **创建清理脚本**:
* 可以在 `server/tools/` 目录下创建一个新脚本,例如 `cleanup_artifacts.py`
2. **定义保留规则**:
* **对于预测结果 (`.json` in `saved_predictions/`)**:
* **基于时间**: 只保留最近30天的记录。
* **基于数量**: 只保留最新的1000条记录。
* **对于模型 (`.pth` in `saved_models/`)**:
* **基于版本**: 只保留每个模型同一种药品、同一种算法最新的3个版本。
* **保留最佳模型**: 始终保留性能最佳的 `best` 版本,不参与自动清理。
3. **执行脚本**:
* 脚本的逻辑是:连接数据库,查询出所有符合清理条件的记录,然后安全地删除硬盘上对应的文件,最后再删除数据库中的条目。
* 这个脚本可以通过服务器的定时任务如Linux的Cron Job或Windows的Task Scheduler设置为每日自动执行。
#### **企业级方案:归档到冷存储**
对于需要长期保留数据以备审计的场景更专业的做法是将旧文件归档至廉价的云对象存储如阿里云OSS, AWS S3等而不是直接删除。数据库中仅更新文件路径指向云端地址即可。