
简介面向时间序列预测与深度学习入门人群这份Python源码包围绕LSTM神经网络完整实现数据清洗、特征提取、建模与预测全流程。项目以空气污染数据集为示例包含可训练的LSTM模型权重和checkpoint文件代码结构清晰下载后无需修改即可直接运行适合课程设计、毕业设计或企业侧快速原型验证。压缩包共125个文件主要由75个py脚本负责数据处理、模型构建与预测调用26个csv提供训练与测试数据15个txt记录说明或日志另有h5、checkpoint等模型持久化文件整体仅5.42MB轻量易部署。目前已有1356人学习下载。对于希望快速跑通LSTM时间序列任务的学习者可从模型保存、数据预处理到预测输出逐步对照阅读节省自行搭建环境与调试的时间。1. 时间序列预测为什么选 LSTM这份源码把数据到预测的链路全打通了做空气污染浓度预测、设备寿命预测、流量预测这类任务时最常踩的坑不是模型不会写而是数据到模型之间的链路断了一截。这份基于 Python 的 LSTM 神经网络时间序列预测源码不是只丢一个 model.py 给你而是把数据清洗、特征提取、建模、预测四条环节全部串好附带的 pollution.csv 是多变量监测数据跑通一次之后换自己的业务表只需要改列名和窗口参数。LSTM 相比 ARIMA 的优势在于能捕捉长时间依赖这也是它现在成了时间序列预测标配模型的原因。适合课程设计需要完整交付、或者刚接触深度学习想做一轮可复现实验的从业者。2. 数据清洗与特征提取从 pollution.csv 到 LSTM 能吃的样本2.1 先看清数据形态时间索引和缺失值不要上来就填打开 pollution.csv你会发现前几行是 year、month、day、hour 四列时间信息后边跟着 pollution、dew、temp、press、wnd_dir、wnd_spd、snow、rain 八列监测指标。wnd_dir 是风向文字NW、SE 这类LSTM 直接吃不了这是第一个要处理的点。第一步要把四列时间合并成 DatetimeIndexpandas 里处理这类格式最稳的方案是用 read_csv 的 parse_dates 参数配合自定义解析函数from pandas import read_csv from datetime import datetime def parse_ts(x): return datetime.strptime(x, %Y %m %d %H) df read_csv(pollution.csv, parse_dates{datetime: [year, month, day, hour]}, index_coldatetime, date_parserparse_ts) df df.drop(columns[No]) df.fillna(0, inplaceTrue) print(df.head())parse_dates 里用字典指定新列名和参与合并的原始列date_parser 告诉 pandas 按%Y %m %d %H这种空格分隔格式解析。No 列在这里是行号没有预测价值直接 drop。缺失值先用 0 填充这是最简单也最粗糙的做法——如果某列连续缺失很长一段0 值会污染 LSTM 的窗口记忆更好的替代是 df.ffill()用上一个有效值向前填充你可以在源码里直接替换这一行。风向这列字符串我一般直接用字典映射成数值不要用 One-Hot原因是 LSTM 输入是浮点矩阵One-Hot 扩出来的维度会稀释其它数值列的特征占比dir_map {NW: 0, NE: 1, SW: 2, SE: 3, cv: 4} df[wnd_dir] df[wnd_dir].map(dir_map).fillna(4)映射后的 wnd_dir 变成一个类别序号风向本身是环形变量0度到360度首尾相接严格说这点映射有信息损失但对小时级空气污染预测影响很小可以接受。2.2 构造监督学习样本look_back 窗口决定模型看多远LSTM 的输入形状是 (样本数, 时间步, 特征数)所以先把时间序列切成过去一段窗口预测下一个时刻的样本对。源码里负责这件事的是 create_datasetimport numpy as np def create_dataset(dataset, look_back1): X, y [], [] for i in range(len(dataset) - look_back - 1): X.append(dataset[i:(i look_back), :]) y.append(dataset[i look_back, 0]) return np.array(X), np.array(y) look_back 24 X, y create_dataset(scaled, look_back) print(X:, X.shape, y:, y.shape)循环的逻辑是i 到 ilook_back 这段切成输入块ilook_back 这一时刻的第 0 列pollution作为标签。look_back 是这份源码里最值得调的参数24 意味着用过去一天的小时数据预测下一个小时。如果你预测的是设备剩余寿命这类慢变量可以调到 48 甚至 72如果是高频交易数据12 以下反而更好。样本数会随着 look_back 增大而锐减。假设原始数据有 4000 小时look_back 取 24样本数大约是 4000-24-13975 个这个衰减在长序列里可以忽略但如果数据本身只有 200 条look_back 取 50 会直接丢掉 1/4 的样本这种情况下宁可选小窗口也别让训练集缩水。2.3 归一化fit 和 transform 必须分开这是第一条红线pollution 是几十的量级press 是 1000 的量级tensor 直接进 LSTM梯度震荡基本是必然的。源码里用的是 MinMaxScaler把每列压到 [0,1]from sklearn.preprocessing import MinMaxScaler scaler MinMaxScaler(feature_range(0, 1)) scaled scaler.fit_transform(values) train_size int(len(scaled) * 0.7) train scaled[:train_size] test scaled[train_size:]这里有个高频翻车点先 fit 全量数据再切分还是先切分再 fit正确顺序是先对训练集 fit再用同一个 scaler transform 测试集。如果拿全量数据去 fit测试集的 min/max 信息提前泄漏到了缩放器里预测误差会被低估看起来很好看一上真实数据就现原形。train/test 切分我这里用了 7:3。做时间序列切分时不能像分类任务那样随机打乱后 30% 按时间顺序就是测试集原因很简单我们要验证的是模型对未来的预测能力不能在验证时把未来数据混进训练窗口。切完之后记得 reshape 成 LSTM 要求的 (samples, timesteps, features) 三维输入这一步漏了keras 直接报维度错误。3. LSTM 模型搭建与参数选择单变量和多变量的差异3.1 从 Input 到 DenseLSTM 层的维度怎么对得上LSTM 层的输入要求是三维张量 (batch_size, time_steps, features)。构造样本时我们指定了 look_back24特征数 features 就是 pollution.csv 里数值列的个数源码里是 8。搭建模型最稳的做法是先打印 X.shape 确认第三个维度再写模型from keras.models import Sequential from keras.layers import LSTM, Dense, Dropout model Sequential() model.add(LSTM(50, return_sequencesTrue, input_shape(X.shape[1], X.shape[2]))) model.add(Dropout(0.2)) model.add(LSTM(50, return_sequencesFalse)) model.add(Dropout(0.2)) model.add(Dense(1)) model.compile(optimizeradam, lossmae) print(model.summary())第一层 LSTM 的 return_sequencesTrue 表示把每个时间步的隐藏状态都输出给下一层这样第二层 LSTM 才能拿到完整的时序信息最后一层 LSTM 改 return_sequencesFalse只输出最后一个时间步的隐藏状态经过 Dense(1) 压缩成单个预测值。神经元数 50 是这套代码的经验起点数据集在几千条量级时 50 足够数据量过万可以试用 100。Dense(1) 没有指定激活函数这是故意的。回归任务的输出层用线性激活默认就是 linear不要加 sigmoid 或 relu——sigmoid 会把输出限制在 0 到 1 之间relu 则会让预测值恒为非负两种都会让模型拟合真实污染浓度时出现系统性偏差。3.2 关键超参数look_back、batch_size、epochs 怎么定这组参数决定了训练时间和过拟合风险。源码里的默认值大概是这样一组look_back 24 batch_size 72 epochs 50 validation_split 0.1batch_size72 很接近一天的样本数24小时 x 3天实际训练时会以 72 个样本为一个梯度更新批次。epochs 设 50配合 EarlyStopping 的话不用太担心过拟合。我的习惯是第一轮先跑到 50 epoch 看完 loss 曲线如果 val_loss 在 30 个 epoch 后就持续反弹再降回去而不是一开始就设 200 无脑跑。validation_split0.1 是在训练集尾部切出 10% 做验证注意 keras 的这个切分也是按顺序从尾部切不会打乱这对时间序列恰好是对的。如果你手动切了 test 又同时在 model.fit 里开 validation_split那验证集和测试集会重叠一部分评估曲线会失真建议只保留一个。3.3 checkpoint 保存训练中断不用从头再来训练跑一天中间断了没保存模型就是白给。源码里有 checkpoint 文件说明用了 ModelCheckpoint 回调from keras.callbacks import ModelCheckpoint checkpoint ModelCheckpoint(checkpoint.h5, monitorval_loss, save_best_onlyTrue, verbose1) history model.fit(train_X, train_y, epochsepochs, batch_sizebatch_size, validation_split0.1, callbacks[checkpoint], verbose2)monitorval_loss 表示只在验证损失创新低时覆盖保存save_best_onlyTrue 保证 checkpoint.h5 始终是表现最好的那版权重。verbose1 会在每个 epoch 结束时打印保存记录方便确认确实触发了保存。如果训练后期 loss 不再下降可以考虑配合 ReduceLROnPlateau在 val_loss 连续 5 个 epoch 不降时把学习率乘以 0.5实测能让收敛更稳from keras.callbacks import ReduceLROnPlateau reduce_lr ReduceLROnPlateau(monitorval_loss, factor0.5, patience5, min_lr0.0001)把 reduce_lr 也加进 callbacks 列表即可。学习率从 adam 默认的 0.001 降下去之后模型往往还能再磨掉一截 loss这是不增加 epochs 就能提点的常用手段。4. 训练预测与评估把链路完整跑一遍4.1 训练脚本从数据加载到模型保存的完整顺序这一节把前面几节的代码按执行顺序串起来。源码的入口是 train.py 这类脚本整体流程是读数据 → 合并时间索引 → 清洗列 → 数值化风向 → 归一化 → 构造样本 → 切分 →建模 → 训练 → 保存 checkpoint。需要注意的前置条件是 Python 3.7 以上依赖 numpy、pandas、scikit-learn、keras/tensorflow。跑之前先确认 GPU 是否被 tensorflow 识别import tensorflow as tf print(tf.test.is_gpu_available())如果没有 GPULSTM 在 CPU 上也能跑只是 epochs 要适当减少。数据量在几千条量级时 CPU 训练也就几分钟到十几分钟不用被 GPU 卡住项目进度。4.2 预测与反归一化模型输出怎么还原成真实数值模型输出的是归一化之后的预测值直接画图是 [0,1] 区间的曲线要还原成真实浓度必须用训练时那个 scaler 做 inverse_transformfrom keras.models import load_model model load_model(checkpoint.h5) y_pred_scaled model.predict(test_X, verbose0) # 构造与原始数据相同列数的矩阵只还原第 0 列 inv_y_pred np.zeros((len(y_pred_scaled), values.shape[1])) inv_y_pred[:, 0] y_pred_scaled[:, 0] y_pred scaler.inverse_transform(inv_y_pred)[:, 0] inv_y_true np.zeros((len(test_y), values.shape[1])) inv_y_true[:, 0] test_y[:len(y_pred)] y_true scaler.inverse_transform(inv_y_true)[:, 0]为什么要造一个全是零的完整列数矩阵再 inverse_transform因为 scaler 在 fit 时记住了每一列的 min 和 maxinverse_transform 要求输入列数一致直接传一列会报维度错误。只还原第 0 列之后从矩阵里把第 0 列取出来就是我们需要的真实量级预测值。4.3 评估指标RMSE、MAE 与可视化对比光看 loss 曲线不够预测值和真实值回到原始量级后用两个指标量化误差from sklearn.metrics import mean_squared_error, mean_absolute_error import math rmse math.sqrt(mean_squared_error(y_true, y_pred)) mae mean_absolute_error(y_true, y_pred) print(fRMSE: {rmse:.2f}, MAE: {mae:.2f})RMSE 对大幅偏差更敏感适合发现偶发的离谱预测MAE 反映平均偏差水平。空气质量浓度场景里RMSE 在 10 以内算可接受20 以上说明模型基本没学到趋势先回去查数据清洗和 look_back。可视化用 matplotlib 画两条曲线测试集前 200 个点就够看趋势是否对齐。5. 常见问题与避坑五个让我折腾半天的坑5.1 预测结果是一条水平直线现象预测曲线几乎不波动画出来就是一条贴近均值的线。 原因这类问题大概率出在输出层或者数据没有做差分。LSTM 对平稳序列的预测容易收敛到均值因为最小化 MSE 的最安全策略就是预测历史均值。 解决先确认 Dense(1) 没加激活函数其次把目标列做一阶差分预测差值再累加回去。源码里可以直接对 pollution 列做 diff把差分结果作为 y预测完成后再逆差分还原。5.2 时间索引解析报错或错位现象read_csv 之后 index 全是 NaT或者前几行数据时间错乱。 原因date_parser 的格式字符串和实际列内容不匹配。pollution.csv 里年月日小时是空格分隔%Y %m %d %H一个字符都不能差月份写成 %y 会解析出 1970 年之类的时间。 解决先只读一列 print 出来看真实格式再写格式串。解析完用 df.index.is_monotonic 检查是否单调递增如果不是需要重新排序再进后续流程。5.3 验证集和测试集重叠现象训练曲线很好看但换新数据预测一塌糊涂。 原因model.fit 开了 validation_split同时手动切了 test 集两者都从序列尾部取数据重叠。 解决二选一。我一般保留手动切分的 test 集model.fit 里不设 validation_split改用 validation_data(val_X, val_y) 显式传入保证三个集合不交叉。5.4 训练 loss 变成 NaN现象训练到第几个 epochloss 突然变成 nan之后一直 nan。 原因数据里有 inf 或极大异常值归一化后仍然存在异常尖峰或者学习率偏大导致梯度爆炸。 解决先对 values 做 np.isfinite 检查把异常值替换掉再把学习率降到 0.0001。如果 data 文件夹里有多份 pollution.csv确认读的是清洗后的那版而不是包含脏数据的原始文件。5.5 checkpoint 加载失败现象load_model 报 Unknown loss function 或结构不匹配。 原因checkpoint 保存时 keras 版本和加载时不一致或者训练中途改过模型结构。save_best_only 模式下覆盖保存的是权重如果模型定义的层数变了权重文件对不上。 解决checkpoint 用 save_weights_onlyTrue 保存纯权重加载前先重建一模一样的模型再 load_weights。这样即使 keras 升级权重文件兼容性也远好于完整模型文件。6. 进阶把单步预测改成多步预测前面的链路每次只预测下一个小时但实际业务里更多是要预测未来 12 小时甚至 24 小时的变化。改法不复杂核心是递归预测把预测出的值塞回输入窗口滚动预测后续时刻。def predict_future(model, last_seq, steps12): future [] current last_seq.copy() for _ in range(steps): pred model.predict(current[np.newaxis, :, :], verbose0) future.append(pred[0, 0]) current np.roll(current, -1, axis0) current[-1, 0] pred[0, 0] # 预测值回填到窗口末端 return future每次循环先对当前窗口 predict 一个值存进 future然后用 np.roll 把窗口整体前移一格把刚预测出的值放到窗口最后。这样模型每次都基于自己上一轮的结果继续预测代价是误差会逐步累积——预测第 1 小时的误差可能只有 3到第 12 小时可能放大到 15。缓解办法是每一步把预测值的置信区间也画出来让业务方知道后段是不可靠的。想提高多步精度可以把训练目标改成一次输出 12 个值Dense(12)让模型学习一个短序列的联合分布而不是递归地自己喂自己。缺点是需要重新构造样本标签代码改动量比递归法大数据量不足时效果反而不如递归。我现在的习惯是短期 3 步以内用直接多步输出超过 6 步用递归并且每次落实验证都会把未来预测值回灌前一步的真实值做一次对比看看误差放大曲线是否在可控范围内。从那以后我每次做时间序列项目都会强制走一遍预测-回灌-观察误差累积的验证流程比任何 loss 曲线都可靠。希望帮到你。本文还有配套的精品资源点击获取