diff --git a/prediction_history.db b/prediction_history.db index 725300c..b2a9488 100644 Binary files a/prediction_history.db and b/prediction_history.db differ diff --git a/saved_predictions/prediction_19549899-43bc-4a16-8a9e-c897d25693fb.json b/saved_predictions/prediction_19549899-43bc-4a16-8a9e-c897d25693fb.json new file mode 100644 index 0000000..c36bb4b --- /dev/null +++ b/saved_predictions/prediction_19549899-43bc-4a16-8a9e-c897d25693fb.json @@ -0,0 +1 @@ +{"product_id": "17002608", "product_name": "云南白药创可贴(便携型)", "model_type": "xgboost", "predictions": [{"date": "2025-04-05", "predicted_sales": 24.257638931274414}, {"date": "2025-04-06", "predicted_sales": 23.752357482910156}, {"date": "2025-04-07", "predicted_sales": 24.069677352905273}, {"date": "2025-04-08", "predicted_sales": 19.112489700317383}, {"date": "2025-04-09", "predicted_sales": 21.332242965698242}, {"date": "2025-04-10", "predicted_sales": 20.065534591674805}, {"date": "2025-04-11", "predicted_sales": 19.03286361694336}, {"date": "2025-04-12", "predicted_sales": 22.03268814086914}, {"date": "2025-04-13", "predicted_sales": 22.380788803100586}, {"date": "2025-04-14", "predicted_sales": 22.927005767822266}, {"date": "2025-04-15", "predicted_sales": 17.80633544921875}, {"date": "2025-04-16", "predicted_sales": 22.225345611572266}, {"date": "2025-04-17", "predicted_sales": 22.632287979125977}, {"date": "2025-04-18", "predicted_sales": 19.119356155395508}, {"date": "2025-04-19", "predicted_sales": 23.014183044433594}, {"date": "2025-04-20", "predicted_sales": 17.28049087524414}, {"date": "2025-04-21", "predicted_sales": 16.84412384033203}, {"date": "2025-04-22", "predicted_sales": 16.275175094604492}, {"date": "2025-04-23", "predicted_sales": 14.114492416381836}], "prediction_data": [{"date": "2025-04-05", "predicted_sales": 24.257638931274414}, {"date": "2025-04-06", "predicted_sales": 23.752357482910156}, {"date": "2025-04-07", "predicted_sales": 24.069677352905273}, {"date": "2025-04-08", "predicted_sales": 19.112489700317383}, {"date": "2025-04-09", "predicted_sales": 21.332242965698242}, {"date": "2025-04-10", "predicted_sales": 20.065534591674805}, {"date": "2025-04-11", "predicted_sales": 19.03286361694336}, {"date": "2025-04-12", "predicted_sales": 22.03268814086914}, {"date": "2025-04-13", "predicted_sales": 22.380788803100586}, {"date": "2025-04-14", "predicted_sales": 22.927005767822266}, {"date": "2025-04-15", "predicted_sales": 17.80633544921875}, {"date": "2025-04-16", "predicted_sales": 22.225345611572266}, {"date": "2025-04-17", "predicted_sales": 22.632287979125977}, {"date": "2025-04-18", "predicted_sales": 19.119356155395508}, {"date": "2025-04-19", "predicted_sales": 23.014183044433594}, {"date": "2025-04-20", "predicted_sales": 17.28049087524414}, {"date": "2025-04-21", "predicted_sales": 16.84412384033203}, {"date": "2025-04-22", "predicted_sales": 16.275175094604492}, {"date": "2025-04-23", "predicted_sales": 14.114492416381836}], "history_data": [{"date": "2025-03-06", "sales": 57.0, "product_id": "17002608", "product_name": "云南白药创可贴(便携型)", "store_id": "GLOBAL", "store_name": "全部店铺-SUM", "weekday": 3, "month": 3, "is_holiday": false, "is_weekend": false, "is_promotion": false, "temperature": 20.0}, {"date": "2025-03-07", "sales": 84.0, "product_id": "17002608", "product_name": "云南白药创可贴(便携型)", "store_id": "GLOBAL", "store_name": "全部店铺-SUM", "weekday": 4, "month": 3, "is_holiday": false, "is_weekend": false, "is_promotion": false, "temperature": 20.0}, {"date": "2025-03-08", "sales": 96.0, "product_id": "17002608", "product_name": "云南白药创可贴(便携型)", "store_id": "GLOBAL", "store_name": "全部店铺-SUM", "weekday": 5, "month": 3, "is_holiday": false, "is_weekend": true, "is_promotion": false, "temperature": 20.0}, {"date": "2025-03-09", "sales": 78.0, "product_id": "17002608", "product_name": "云南白药创可贴(便携型)", "store_id": "GLOBAL", "store_name": "全部店铺-SUM", "weekday": 6, "month": 3, "is_holiday": false, "is_weekend": true, "is_promotion": false, "temperature": 20.0}, {"date": "2025-03-10", "sales": 55.0, "product_id": "17002608", "product_name": "云南白药创可贴(便携型)", "store_id": "GLOBAL", "store_name": "全部店铺-SUM", "weekday": 0, "month": 3, "is_holiday": false, "is_weekend": false, "is_promotion": false, "temperature": 20.0}, {"date": "2025-03-11", "sales": 67.0, "product_id": "17002608", "product_name": "云南白药创可贴(便携型)", "store_id": "GLOBAL", "store_name": "全部店铺-SUM", "weekday": 1, "month": 3, "is_holiday": false, "is_weekend": false, "is_promotion": false, "temperature": 20.0}, {"date": "2025-03-12", "sales": 78.0, "product_id": "17002608", "product_name": "云南白药创可贴(便携型)", "store_id": "GLOBAL", "store_name": "全部店铺-SUM", "weekday": 2, "month": 3, "is_holiday": false, "is_weekend": false, "is_promotion": false, "temperature": 20.0}, {"date": "2025-03-13", "sales": 66.0, "product_id": "17002608", "product_name": "云南白药创可贴(便携型)", "store_id": "GLOBAL", "store_name": "全部店铺-SUM", "weekday": 3, "month": 3, "is_holiday": false, "is_weekend": false, "is_promotion": false, "temperature": 20.0}, {"date": "2025-03-14", "sales": 66.0, "product_id": "17002608", "product_name": "云南白药创可贴(便携型)", "store_id": "GLOBAL", "store_name": "全部店铺-SUM", "weekday": 4, "month": 3, "is_holiday": false, "is_weekend": false, "is_promotion": false, "temperature": 20.0}, {"date": "2025-03-15", "sales": 83.0, "product_id": "17002608", "product_name": "云南白药创可贴(便携型)", "store_id": "GLOBAL", "store_name": "全部店铺-SUM", "weekday": 5, "month": 3, "is_holiday": false, "is_weekend": true, "is_promotion": false, "temperature": 20.0}, {"date": "2025-03-16", "sales": 83.0, "product_id": "17002608", "product_name": "云南白药创可贴(便携型)", "store_id": "GLOBAL", "store_name": "全部店铺-SUM", "weekday": 6, "month": 3, "is_holiday": false, "is_weekend": true, "is_promotion": false, "temperature": 20.0}, {"date": "2025-03-17", "sales": 66.0, "product_id": "17002608", "product_name": "云南白药创可贴(便携型)", "store_id": "GLOBAL", "store_name": "全部店铺-SUM", "weekday": 0, "month": 3, "is_holiday": false, "is_weekend": false, "is_promotion": false, "temperature": 20.0}, {"date": "2025-03-18", "sales": 62.0, "product_id": "17002608", "product_name": "云南白药创可贴(便携型)", "store_id": "GLOBAL", "store_name": "全部店铺-SUM", "weekday": 1, "month": 3, "is_holiday": false, "is_weekend": false, "is_promotion": false, "temperature": 20.0}, {"date": "2025-03-19", "sales": 64.0, "product_id": "17002608", "product_name": "云南白药创可贴(便携型)", "store_id": "GLOBAL", "store_name": "全部店铺-SUM", "weekday": 2, "month": 3, "is_holiday": false, "is_weekend": false, "is_promotion": false, "temperature": 20.0}, {"date": "2025-03-20", "sales": 53.0, "product_id": "17002608", "product_name": "云南白药创可贴(便携型)", "store_id": "GLOBAL", "store_name": "全部店铺-SUM", "weekday": 3, "month": 3, "is_holiday": false, "is_weekend": false, "is_promotion": false, "temperature": 20.0}, {"date": "2025-03-21", "sales": 62.0, "product_id": "17002608", "product_name": "云南白药创可贴(便携型)", "store_id": "GLOBAL", "store_name": "全部店铺-SUM", "weekday": 4, "month": 3, "is_holiday": false, "is_weekend": false, "is_promotion": false, "temperature": 20.0}, {"date": "2025-03-22", "sales": 85.0, "product_id": "17002608", "product_name": "云南白药创可贴(便携型)", "store_id": "GLOBAL", "store_name": "全部店铺-SUM", "weekday": 5, "month": 3, "is_holiday": false, "is_weekend": true, "is_promotion": false, "temperature": 20.0}, {"date": "2025-03-23", "sales": 77.0, "product_id": "17002608", "product_name": "云南白药创可贴(便携型)", "store_id": "GLOBAL", "store_name": "全部店铺-SUM", "weekday": 6, "month": 3, "is_holiday": false, "is_weekend": true, "is_promotion": false, "temperature": 20.0}, {"date": "2025-03-24", "sales": 57.0, "product_id": "17002608", "product_name": "云南白药创可贴(便携型)", "store_id": "GLOBAL", "store_name": "全部店铺-SUM", "weekday": 0, "month": 3, "is_holiday": false, "is_weekend": false, "is_promotion": false, "temperature": 20.0}, {"date": "2025-03-25", "sales": 65.0, "product_id": "17002608", "product_name": "云南白药创可贴(便携型)", "store_id": "GLOBAL", "store_name": "全部店铺-SUM", "weekday": 1, "month": 3, "is_holiday": false, "is_weekend": false, "is_promotion": false, "temperature": 20.0}, {"date": "2025-03-26", "sales": 83.0, "product_id": "17002608", "product_name": "云南白药创可贴(便携型)", "store_id": "GLOBAL", "store_name": "全部店铺-SUM", "weekday": 2, "month": 3, "is_holiday": false, "is_weekend": false, "is_promotion": false, "temperature": 20.0}, {"date": "2025-03-27", "sales": 73.0, "product_id": "17002608", "product_name": "云南白药创可贴(便携型)", "store_id": "GLOBAL", "store_name": "全部店铺-SUM", "weekday": 3, "month": 3, "is_holiday": false, "is_weekend": false, "is_promotion": false, "temperature": 20.0}, {"date": "2025-03-28", "sales": 63.0, "product_id": "17002608", "product_name": "云南白药创可贴(便携型)", "store_id": "GLOBAL", "store_name": "全部店铺-SUM", "weekday": 4, "month": 3, "is_holiday": false, "is_weekend": false, "is_promotion": false, "temperature": 20.0}, {"date": "2025-03-29", "sales": 69.0, "product_id": "17002608", "product_name": "云南白药创可贴(便携型)", "store_id": "GLOBAL", "store_name": "全部店铺-SUM", "weekday": 5, "month": 3, "is_holiday": false, "is_weekend": true, "is_promotion": false, "temperature": 20.0}, {"date": "2025-03-30", "sales": 100.0, "product_id": "17002608", "product_name": "云南白药创可贴(便携型)", "store_id": "GLOBAL", "store_name": "全部店铺-SUM", "weekday": 6, "month": 3, "is_holiday": false, "is_weekend": true, "is_promotion": false, "temperature": 20.0}, {"date": "2025-03-31", "sales": 72.0, "product_id": "17002608", "product_name": "云南白药创可贴(便携型)", "store_id": "GLOBAL", "store_name": "全部店铺-SUM", "weekday": 0, "month": 3, "is_holiday": false, "is_weekend": false, "is_promotion": false, "temperature": 20.0}, {"date": "2025-04-01", "sales": 112.0, "product_id": "17002608", "product_name": "云南白药创可贴(便携型)", "store_id": "GLOBAL", "store_name": "全部店铺-SUM", "weekday": 1, "month": 4, "is_holiday": false, "is_weekend": false, "is_promotion": false, "temperature": 20.0}, {"date": "2025-04-02", "sales": 56.0, "product_id": "17002608", "product_name": "云南白药创可贴(便携型)", "store_id": "GLOBAL", "store_name": "全部店铺-SUM", "weekday": 2, "month": 4, "is_holiday": false, "is_weekend": false, "is_promotion": false, "temperature": 20.0}, {"date": "2025-04-03", "sales": 115.0, "product_id": "17002608", "product_name": "云南白药创可贴(便携型)", "store_id": "GLOBAL", "store_name": "全部店铺-SUM", "weekday": 3, "month": 4, "is_holiday": false, "is_weekend": false, "is_promotion": false, "temperature": 20.0}, {"date": "2025-04-04", "sales": 84.0, "product_id": "17002608", "product_name": "云南白药创可贴(便携型)", "store_id": "GLOBAL", "store_name": "全部店铺-SUM", "weekday": 4, "month": 4, "is_holiday": false, "is_weekend": false, "is_promotion": false, "temperature": 20.0}], "analysis": {"trend": {"slope": -0.35340793676543664, "trend_type": "下降", "r_squared": 0.4464575969990724, "p_value": 0.001766767507323499, "volatility": 0.1417623496278433, "volatility_level": "中"}, "statistics": {"mean": 20.435530411569697, "median": 21.332242965698242, "min": 14.114492416381836, "max": 24.257638931274414, "std": 2.8969888070353678, "q1": 18.419599533081055, "q3": 22.77964687347412}, "day_over_day": [-2.082978684759045, 1.335951053378298, -20.595156220444913, 11.614150224206519, -5.937999000200192, -5.1464912136450165, 15.76128839201732, 1.579928241192423, 2.4405617224983955, -22.33466668286142, 24.817066796006017, 1.8309833046727726, -15.521770609186694, 20.37111949472713, -24.91373323189177, -2.5252004590751786, -3.3777283468151222, -13.275941215151416], "influencing_factors": {"product_id": "17002608", "model_type": "xgboost", "feature_count": 7, "important_features": ["价格", "周末", "节假日"]}, "explanation": "xgboost模型对产品17002608的预测分析:\n预测显示销量整体呈下降趋势,平均每天下降约0.35个单位。\n预测期内销量波动性中,表明销量有一定波动,但整体可控。\n预测期内平均日销量为20.44个单位,最高日销量为24.26个单位,最低日销量为14.11个单位。\n\n主要影响因素包括:价格, 周末, 节假日。"}} \ No newline at end of file diff --git a/server/api.py b/server/api.py index 79c09ec..fa76b31 100644 --- a/server/api.py +++ b/server/api.py @@ -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) diff --git a/server/core/config.py b/server/core/config.py index b9e828b..7bbc97f 100644 --- a/server/core/config.py +++ b/server/core/config.py @@ -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: diff --git a/项目快速上手指南.md b/项目快速上手指南.md index b263f4d..114c5fd 100644 --- a/项目快速上手指南.md +++ b/项目快速上手指南.md @@ -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) -完成以上步骤后,重启服务,你就可以在界面上选择并使用你的新算法了。 \ No newline at end of file +**问题**: +随着时间推移,`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等),而不是直接删除。数据库中仅更新文件路径指向云端地址即可。 \ No newline at end of file