在探索时序数据预测的过程中,我选择了共享单车需求预测作为实践方向 —— 共享单车的骑行量随时间(小时、日期、季节)呈现明显的周期性规律,非常适合用 LSTM(长短期记忆网络)来建模。整个项目的完整代码已上传至 GitHub:https://github.com/Lyan-X/LSTM-based-BSDP,接下来我会梳理搭建这个项目过程中,针对各个环节的思考和落地细节。
一、项目初步介绍
1.项目背景与目标
- 现实痛点:共享单车作为城市短途交通补充,普遍存在 “高峰无车、平峰闲置” 的供需失衡问题,精准的小时级需求预测能为车辆调度、站点规划提供数据支撑;
- 核心目标:基于连续 24 小时的多维度特征(季节、温度、小时等),构建模型预测第 25 小时的共享单车骑行量,最终实现可视化 Web 部署;
- 应用价值:验证 LSTM 模型在非线性时序数据中的拟合能力,为同类时序预测场景(如电力负荷预测、客流量预测)提供参考。
2.项目整体流程
项目整体流程
3.技术栈选型
| 核心语言 | Python 3.9 | 数据处理生态完善,机器学习库兼容性强,兼顾科研与工程落地需求 |
| 模型框架 | TensorFlow 2.15 | 封装完整,LSTM 层 API 简洁直观,支持动态图训练,版本稳定可避免兼容问题 |
| 数据处理 | Pandas 1.5.3、NumPy 1.26.4 | Pandas 高效处理时序数据,NumPy 适配矩阵运算,与 TensorFlow 版本精准兼容 |
| 特征工程 / 评估 | Scikit-learn 1.3.2 | 提供标准化的归一化工具、模型评估指标(MAE/RMSE),API 简洁易集成 |
| 可视化 / 部署 | Matplotlib 3.7.1、Django 4.2.9 | Matplotlib 支持结果可视化,Django 轻量易上手,适合快速搭建预测 Web 界面 |
4.项目目录结构
BSDP/ # 项目根目录
├── data/ # 数据存储目录
│ ├── raw/ # 原始数据集(train.csv/test.csv,不提交代码仓库)
│ ├── x_train.npy # 预处理后训练特征数据
│ ├── y_train.npy # 预处理后训练目标数据
│ ├── x_val.npy # 预处理后验证特征数据
│ └── y_val.npy # 预处理后验证目标数据
├── models/ # 模型相关文件
│ ├── train_lstm.py # 模型训练核心代码
│ └── bike_lstm_model.h5 # 训练完成的模型(本地生成,不提交仓库)
├── utils/ # 工具函数目录
│ ├── scaler_x.pkl # 特征归一化器
│ ├── scaler_y.pkl # 目标变量归一化器
│ └── data_preprocess.py # 数据预处理代码
├── results/ # 实验结果存储
│ └── prediction_result.png # 预测效果对比图
├── web/ # Django Web部署模块(后续章节详解)
├── .gitignore # 代码仓库过滤规则(屏蔽大文件/冗余文件)
├── requirements.txt # 依赖库版本清单(确保环境可复现)
└── README.md # 项目说明文档(含运行指南)
一、数据预处理:时序模型的基础考量
搭建预测模型的第一步,我优先聚焦于数据层面的处理 —— 时序模型对数据格式、特征有效性和数据分布的敏感度远高于传统模型,这也是我在预处理阶段重点思考的问题。
1. 特征工程:从业务角度筛选有效维度
数据集信息(train.csv)
原始数据集包含 datetime、season、holiday 等字段,我首先分析了各字段与骑行量(count)的关联性:
- 时间特征拆分:datetime 字段是核心,我考虑到小时、星期、月份是影响骑行量的关键时间维度(比如早高峰 / 晚高峰骑行量高、周末与工作日差异大),因此将 datetime 解析为 hour、weekday、month 三个特征;
- 无关字段剔除:仅保留 season、holiday、workingday、weather、temp、atemp、humidity、windspeed、hour、weekday、month 共 11 个特征,确保输入维度聚焦于对骑行量有实际影响的因素。
2. 归一化:适配 LSTM 的数值尺度要求
LSTM 的神经元激活函数对输入数值的尺度敏感,若特征值范围差异过大(比如温度和风速),会导致模型收敛慢、精度低。因此我选择 MinMaxScaler 将特征和目标值(骑行量)都归一化到 0-1 区间,同时将归一化器(scaler_x、scaler_y)保存到本地,方便后续预测时还原真实的骑行量数值。
3. 时序序列构造:避免数据泄露的核心设计
时序预测的核心是 “用历史数据预测未来”,我确定了 “前 24 小时的特征数据预测下 1 小时骑行量” 的序列规则(24 小时贴合日周期规律,符合共享单车的使用场景)。
在构造序列时,我特别注意按时间顺序划分训练集和验证集(训练集占 80%、验证集占 20%),而非随机划分 —— 这是我在搭建过程中意识到的关键:随机划分会导致 “未来数据泄露到训练集”,完全违背时序预测的逻辑。
最终处理后的训练集形状为 (样本数,24, 11),验证集保持一致的维度,所有预处理后的数据都保存为 npy 格式,供后续模型训练调用。
# ===================== 1. 加载预处理后的时序数据 =====================
# 数据形状:(样本数, 24时间步, 11特征),对应24小时数据预测下1小时骑行量
x_train = np.load("../data/x_train.npy")
y_train = np.load("../data/y_train.npy")
x_val = np.load("../data/x_val.npy")
y_val = np.load("../data/y_val.npy")
二、LSTM 模型搭建:适配场景的结构设计
在模型结构设计上,我没有直接套用现成的 LSTM 模板,而是结合共享单车需求预测的特点做了针对性调整:
1. 网络结构的思考
我设计了双层 LSTM 的结构,核心考量是平衡模型拟合能力和过拟合风险:
- 第一层 LSTM:设置 64 个神经元,return_sequences=True(因为后续接第二层 LSTM,需要接收序列输出),输入形状严格匹配预处理后的序列维度 (24, 11);
- Dropout 层:在两层 LSTM 后都加入 0.2 的 Dropout,这是我考虑到深层网络容易过拟合,通过随机丢弃部分神经元来提升模型泛化能力;
- 第二层 LSTM:设置 32 个神经元,return_sequences=False(后续接全连接层,只需输出最终特征);
- 全连接与输出层:最后加入 16 个神经元的 Dense 层(ReLU 激活)进一步提升拟合能力,输出层仅 1 个神经元(对应骑行量的数值预测,回归任务)。
# ===================== 2. 搭建LSTM模型 =====================
# 简单的双层LSTM结构,适合共享单车需求预测的时序数据
model = tf.keras.Sequential([
# 第一层LSTM,64个神经元,输入为24小时的11个特征
tf.keras.layers.LSTM(64, return_sequences=True, input_shape=(24, 11)),
tf.keras.layers.Dropout(0.2), # 防止模型过拟合
# 第二层LSTM,32个神经元
tf.keras.layers.LSTM(32, return_sequences=False),
tf.keras.layers.Dropout(0.2),
# 全连接层,提升模型拟合能力
tf.keras.layers.Dense(16, activation="relu"),
# 输出层:预测骑行量的数值
tf.keras.layers.Dense(1)
])
2. 编译与训练的参数选择
- 损失函数与优化器:回归任务的核心是最小化预测值与真实值的误差,因此选择均方误差(MSE)作为损失函数;优化器选用 Adam(自适应学习率,无需手动调参,适配 LSTM 的训练特性); # ===================== 3. 编译模型 =====================
# 回归任务用均方误差作为损失函数,Adam优化器调整参数
model.compile(optimizer="adam", loss="mean_squared_error") - 早停机制:我在训练时加入了 EarlyStopping 回调函数,监控验证集损失(val_loss),若 3 轮训练后验证集损失无提升则停止训练,并恢复最优权重 —— 这是我为了避免模型过度训练、过拟合验证集而设计的策略,训练时设置 batch_size=32、epochs=20(实际会因早停提前终止)。 # ===================== 4. 训练模型(加早停避免过拟合) =====================
# 早停设置:验证集效果3轮没提升就停止,避免训练过度
early_stop = tf.keras.callbacks.EarlyStopping(
monitor="val_loss",
patience=3,
restore_best_weights=True
)# 开始训练模型
history = model.fit(
x_train, y_train,
batch_size=32,
epochs=20,
validation_data=(x_val, y_val),
callbacks=[early_stop]
)
3. 模型保存
训练完成后,我将模型保存为 h5 格式(../models/bike_lstm_model.h5),目的是后续能直接集成到 Django Web 系统中,实现实时预测调用。
# ===================== 5. 保存训练好的模型 =====================
# 保存为h5格式,供后续Django Web系统调用
model.save("../models/bike_lstm_model.h5")
print("模型已保存到 models/bike_lstm_model.h5")
三、模型评估与可视化:验证预测效果
模型训练完成后,我需要直观验证预测效果,因此做了以下操作:
1. 预测值还原
由于训练阶段对目标变量(骑行量)做了 0-1 区间的归一化处理,模型直接输出的预测结果是归一化后的数值,无法直接反映真实的骑行量规模。因此我先加载预处理阶段保存的 scaler_y(目标变量归一化器),再对模型预测出的验证集结果(内存中的变量,非文件)和验证集真实值(y_val)分别做反归一化处理,将数值还原为实际的骑行量(辆),确保后续计算的误差指标能贴合实际业务场景。
2. 误差计算
我选择了平均绝对误差(MAE)和均方根误差(RMSE)两个指标:
- MAE 反映预测值与真实值的平均偏差(单位:辆),能直观体现 “预测偏差有多少辆”;
- RMSE 对大误差更敏感,能反映模型的极端预测错误情况。
- 最终评估结果:平均绝对误差(MAE):88.80 辆,均方根误差(RMSE):126.02 辆,整体误差处于合理范围,能较好捕捉骑行量的变化规律。
3. 可视化对比
为了更直观看到预测效果,我绘制了前 100 小时的真实骑行量与预测骑行量对比图:
- 解决 Matplotlib 中文显示问题(设置 SimHei 字体、关闭负号显示异常);
- 标注清晰的坐标轴(x 轴:时间 / 小时,y 轴:骑行量 / 辆),加入网格和图例,最终将图片保存到../results/prediction_result.png。
预测效果可视化
# ===================== 6. 模型评估与可视化 =====================
# 加载归一化器,把预测值还原成真实的骑行量数值
scaler_y = joblib.load("../utils/scaler_y.pkl")
y_pred_scaled = model.predict(x_val)
y_pred = scaler_y.inverse_transform(y_pred_scaled)
y_true = scaler_y.inverse_transform(y_val.reshape(-1, 1)) # 补充维度适配反归一化
# 计算预测误差
mae = mean_absolute_error(y_true, y_pred)
rmse = np.sqrt(mean_squared_error(y_true, y_pred))
print("\\n模型预测效果:")
print(f"平均绝对误差:{mae:.2f} 辆")
print(f"均方根误差:{rmse:.2f} 辆")
# 解决中文显示问题
plt.rcParams["font.family"] = ["SimHei"]
plt.rcParams["axes.unicode_minus"] = False
# 绘制预测结果对比图
plt.figure(figsize=(12, 6))
plt.plot(y_true[:100], label="真实骑行量", color="blue", linewidth=1.5)
plt.plot(y_pred[:100], label="预测骑行量", color="red", alpha=0.7, linewidth=1.5)
# 坐标轴标注(带明确单位)
plt.title("LSTM模型共享单车需求预测效果(前100小时)")
plt.xlabel("时间(小时)")
plt.ylabel("骑行量(辆)")
plt.legend()
plt.grid(True, alpha=0.3)
plt.savefig("../results/prediction_result.png")
plt.show()
五、项目当前进度与后续规划
1. 现阶段完成情况(项目初期核心落地)
当前项目已完成核心预测流程的搭建,具体包括:
- 数据预处理模块:实现公开数据集导入、特征工程、时序序列构造与数据存储;
- 需求预测模块:完成 LSTM 模型的设计、训练、评估与保存,实现小时级骑行量预测;
- 基础支撑:生成依赖清单(requirements.txt)、规范项目目录结构,确保代码可复现。
2. 与项目要求的差距
结合整体项目目标(如毕设 / 完整系统要求),当前仍有以下核心功能待补充:
- 空间维度拓展:未实现 “区域划分” 预测,当前仅输出整体骑行量,需补充站点 / 区域维度的细分预测;
- 多模型对比:未实现 BP 神经网络等其他模型,缺乏不同模型的性能对比分析;
- 运维管理模块:未开发可视化管理界面、调度任务生成、供需热力图等运维功能;
- 系统支撑模块:缺少多角色权限管理、数据加密备份、预测结果导出等系统级功能;
- Web 部署落地:尚未将模型集成到 Django,未实现前端交互与实时预测。
3. 未来补充计划
后续将按以下方向完善项目,确保满足完整系统要求:
六、项目小结
整个搭建过程中,我核心围绕 “如何让 LSTM 模型适配共享单车需求预测的时序特性” 展开思考:从数据预处理时的特征筛选、序列构造(避免数据泄露),到模型结构的分层设计(双层 LSTM+Dropout 防过拟合),再到训练策略(早停机制)和评估方式(业务化的误差指标 + 可视化),每一步都是针对 “时序预测的核心痛点” 和 “共享单车的业务场景” 调整的。
所有代码已开源至 GitHub:https://github.com/Lyan-X/LSTM-based-BSDP,感兴趣的可以自行下载调试,也欢迎交流优化思路。
