ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

交通流量预测实战:LSTM、GRU、CNN三模型源码全解析

交通流量预测实战:LSTM、GRU、CNN三模型源码全解析 简介面向深度学习入门者与交通流量预测初学者这套实战项目源码实现了基于长短期记忆网络、门控循环单元与卷积神经网络的交通流量预测全流程包含数据预处理、模型定义、训练评估与性能可视化适合课程设计、毕业设计或算法对比研究作为基线参考。压缩包共16个文件大小仅1.18MB8个Python脚本对应主程序、配置、数据加载及各模型结构2个npz文件存放训练与测试数据3张PNG图片展示不同模型在统一超参数下的损失与评价指标曲线txt日志与docx数据说明则帮助快速定位代码模块和数据集含义。目前已有92人学习下载项目代码结构清晰、注释友好并配有作者在CSDN发布的对应博客说明读者既能按步骤复现三种模型的预测结果也可自行调整超参数或替换数据集进行扩展实验直观对比循环神经网络不同变体的表现差异。1. 这三份源码能让你少走两个月弯路LSTM、GRU、CNN 跑交通流量预测在深度学习交通流量预测这个方向上论文代码和能跑通的代码是两回事。我见过太多人卡在读不懂的复杂模型、盯着一张不收敛的 loss 曲线发呆。这份资源的价值在于它把 LSTM、GRU、CNN 三个模型放进同一个工程共用一条数据管线你在一个项目里就能完成三种结构的选择对比。适合三种人准备做课程设计的在校生、想把流量预测当基线模型的实习生、以及需要快速验证思路的初级算法工程师。整个项目从数据预处理、模型训练到性能指标可视化是一条完整的链路代码结构干净不是那种打开就劝退的研究代码。我拆这份资源时最大的感受是工程组织方式比模型本身更值得学把数据、模型、配置分开是新手最应该养成的习惯。2. 先把数据、模型和运行链路搞清楚一个工程、三种模型是哪来的2.1 文件清单与角色main.py 是入口其它文件各管一摊拿到解压后的文件夹先别急着运行。我习惯先把每个文件的职责认一遍再动手。这份资源里的文件组织是教科书式的入口、配置、数据加载、模型定义各自独立新手跟着这个结构走一遍基本就理解了深度学习工程的最小分工。文件清单里值得重点关注的是这几个main.py 是总入口训练和评测都从这里启动configuration.py 存超参数学习率、batch size、隐藏层维度都在这里改data_loader.py 负责把 npz 原始数据切成训练用的时间窗口func.py 包含一些工具函数和指标计算。模型则分为 gru.py、lstm.py、cnn_gru.py、cnn_lstm.py 四个文件前两个是单模型后两个是混合模型。这种命名方式很直白一眼就能看出哪个文件对应哪个结构。我拆这个项目时注意到一个细节数据文件是 volume_train.npz 和 volume_test.npz还有一份「数据说明.docx」。这是纽约出租车流量数据的常见格式。数据以 npz 压缩格式存储键名通常是 data 和 label 之类加载时用 np.load 就能读。模型权重虽然没直接给但训练完会自动保存第一次运行不要期望有现成权重可以加载。2.2 从 volume_train.npz 到 Batchdata_loader.py 怎么切时间窗口交通流量预测的本质是时序问题用过去若干时间步的流量预测未来一个或多个时间步的流量。data_loader.py 的核心工作就是这个切窗操作。这套代码里默认的做法是滑窗采样举个例子如果预测步长是 12那输入就是前 12 个时间步的流量序列标签是第 13 个时间步的流量值。# data_loader.py 中的核心逻辑简化示意 def generate_dataset(data, time_step12): x_data, y_data [], [] for i in range(len(data) - time_step): x_data.append(data[i:itime_step]) # 前 time_step 个时间步作为输入 y_data.append(data[itime_step]) # 下一个时间步作为标签 return np.array(x_data), np.array(y_data)这里有两个参数需要理解。time_step 是时间窗口长度对应你拿多少历史数据去预测未来这个值在交通流量预测里一般取 6、12、24对应半小时、一小时、两小时的粒度。第二点是切片方式是重叠的每一步只往后挪一格而不是跳跃式切割。重叠采样能最大程度利用有限的数据量代价是相邻样本之间存在相关性这在小数据集上会让评估结果看起来偏乐观。我一般会建议新手先把 time_step 设成 12 跑一遍因为 12 这个值在交通流量数据上是一个黄金默认值。如果换成小时级数据那就要根据你的采样频率调整5 分钟一条数据就用 12 代表一小时。这块代码里没有做标准化但数据本身已经是流量计数量纲比较稳定。2.3 三种模型的结构差异LSTM、GRU 管时序CNN 管局部特征这份资源同时给了 LSTM、GRU、CNN 以及它们的混合结构这在新手项目里非常少见。为什么放在一起对比因为流量数据同时具备时序依赖性和局部周期性不同模型抓取的特征类型不同。LSTM 和 GRU 同属循环神经网络家族。LSTM 靠三个门控机制——输入门、遗忘门、输出门——控制信息流动擅长捕捉长期依赖GRU 把门控简化为两个门——更新门和重置门参数量更少训练更快在小数据集上往往表现不输 LSTM。在这个项目里两者用的是同一套输入格式三维张量 (batch_size, time_step, feature_dim)。feature_dim 代表每个时间步的特征数如果只用流量一个维度那经过 data_loader 处理后的输入 shape 通常是 (batch_size, time_step, 1)。CNN 在这个项目里的用法不太一样。它不是一维卷积直接遍历时间轴而是作为特征提取器接在序列模型前面或者对序列做变换。cnn_lstm.py 和 cnn_gru.py 这两个混合模型的做法是先用一维卷积在时间维度上滑动提取局部模式把降维后的特征再送进 LSTM 或 GRU。这相当于先用 CNN 做了一次特征筛选让后面的循环结构少背负担。如果你的数据本身噪声大混合结构通常比纯 LSTM 表现更好因为 CNN 相当于先做了一层带学习参数的平滑滤波。我拆这份资源时最深的体会是别一上来就追求复杂模型。先用这两个单模型把管线跑通再对比混合模型看收益这是最节省时间的路线。卡在哪个模型上永远先怀疑数据问题再怀疑模型本身。3. 跑通默认配置从解压到画出第一张指标图3.1 环境准备Torch 版本、依赖和 CUDA 检查这个项目基于 PyTorch 构建环境准备是最简单的一步。先确认你的 Python 版本建议 3.8 到 3.10 之间太低或太高都会遇到兼容性问题。安装依赖时我习惯先建一个独立虚拟环境避免污染系统 Python。# 构建虚拟环境并安装 PyTorch python -m venv traffic_env source traffic_env/bin/activate # Windows 下使用 traffic_env\Scripts\activate pip install torch numpy matplotlib安装 torch 时要注意版本匹配。如果你有 NVIDIA 显卡去 PyTorch 官网按 CUDA 版本选安装命令不要用 pip install torch 默认装 CPU 版那会浪费你的显卡。没有 GPU 也不用担心这份资源的数据量不大CPU 跑也能在可接受时间内完成训练。启动前检查一下设备# 在 main.py 或任意脚本中检查运行设备 import torch print(torch.cuda.is_available()) print(torch.cuda.get_device_name(0) if torch.cuda.is_available() else 使用 CPU 训练)参数说明torch.cuda.is_available() 返回 True 说明 GPU 可用False 则只能走 CPU。如果你有显卡但返回 False优先查驱动和 torch 版本的 CUDA 编译版本是否匹配。这个问题占新手段位排行的前三名。3.2 跑一次完整训练启动命令和 log.txt 的读法环境准备好后直接运行 main.py 就能启动训练。这个项目的默认配置写好了学习率 0.001、batch_size 64、隐藏层维度 64、dropout 0.5从文件名里的 lr0.001_b64_h64_d0.5 就能看出来。这些是原作者调过的一组合理参数新手第一次跑建议原封不动不要自行发挥。# 在项目根目录下启动训练 python main.py训练开始后终端会不断输出每个 epoch 的 loss 和验证集指标。同时项目里预先生成了一个 log.txt记录了一轮完整的训练日志。我第一次跑的时候仔细对比了终端的实时输出和 log.txt 有什么区别——前者是实时刷屏后者是完整存档里面对应着每个 epoch 的 loss 和最终指标。这些输出内容应该在 main.py 中被 print 出来这也方便你重定向到自己的日志文件# 将终端输出同时写入日志文件和终端 python main.py 21 | tee my_first_run.log跑完一轮后项目根目录会出现与模型同名的 metrics.png 文件比如 lstm_lr0.001_b64_h64_d0.5_metrics.png展示的是训练集和验证集的 loss 曲线以及预测值与真实值的对比图。看见这张图生成说明你的环境从数据到训练链路是通的。3.3 改参数的正确姿势configuration.py 里只改结构不改数字新手最常见的翻车方式是什么直接改源码把代码改得面目全非然后找不回默认配置。我从这个项目学到的习惯是所有超参数在 configuration.py 里统一管模型文件里不该出现裸数字。改参数前先读一下 configuration.py把学习率、隐藏层维度、dropout、batch_size、epoch 数量这些统一修改再跑 main.py 时它就会自动读取新配置。这个做法值得一直沿用下去。# configuration.py 中的典型超参数设置示例 config { model: lstm, # 可选: lstm / gru / cnn_gru / cnn_lstm learning_rate: 0.001, batch_size: 64, hidden_size: 64, dropout: 0.5, epochs: 100, }注意 model 这个键切换模型不用改代码只改这个字符串就能从 LSTM 换成 GRU 或混合模型。很多新手不知道这个设置用了很长时间都在跑同一个模型。我建议每修改一个参数就做一次对比实验先跑一组默认参数再只改一个变量这样才能看出每个参数的真实影响。4. 怎么把三个模型当基线好好用训练、评估和可视化里的门道4.1 训练流程与 model.train() 的细节dropout 在训练和评估时的差异代码里每组模型都对应独立的 py 文件比如 lstm.py 包含网络结构定义和训练过程。这里要留意 PyTorch 中模型有两种模式——训练模式和评估模式通过 model.train() 与 model.eval() 切换。dropout 只在训练模式下生效评估时会被关闭。如果加载权重后忘记切到 eval 模式评估结果会像带噪声一样不稳定每次跑出来的指标都不同。# 训练过程的主循环逻辑 model.train() for epoch in range(config[epochs]): for batch_x, batch_y in train_loader: optimizer.zero_grad() output model(batch_x) loss loss_fn(output, batch_y) loss.backward() optimizer.step() # 评估阶段必须切换模式 model.eval() with torch.no_grad(): val_output model(val_x)关键的逻辑有两点。其一是 optimizer.zero_grad() 必须在每次反向传播之前调用否则梯度会累加导致训练震荡。其二是评估时用 torch.no_grad() 包裹计算图这不参与梯度计算能省内存而且算得快。不少新手在评估时忘记这两步结果模型在测试集上表现不稳定还反过来怀疑实现有问题。4.2 评估标准MAE、MSE、RMSE 怎么影响你对「哪个模型好」的判断这份资源没有只给你训练代码它把评估指标也封装好了。在流量预测任务上几个核心指标定义如下MAE 是平均绝对误差体现预测值与真实值的平均偏离程度MSE 是均方误差对较大误差更敏感RMSE 是 MSE 的平方根与 MAE 相比更放大离群点的作用。这三个指标数值越低越好。# 评估指标计算示例 import numpy as np def mae(y_true, y_pred): return np.mean(np.abs(y_true - y_pred)) def rmse(y_true, y_pred): return np.sqrt(np.mean((y_true - y_pred) ** 2))在这个项目里模型的输出是流量值不是概率值直接用这些指标就可以了。对比模型时不要只看 MAE要同时看 RMSE——如果两个模型 MAE 相近但 RMSE 差距大说明误差分布不同RMSE 高的那个可能在个别时间点预测偏差较大。交通流量预测在实际应用中峰值时刻的预测误差通常比平峰时刻更受关注。4.3 性能图命名里的信息lr0.001_b64_h64_d0.5_metrics.png 都在说什么项目里包含了三份现成的性能展示图命名格式是「模型名_超参数_metrics.png」。比如 lstm_lr0.001_b64_h64_d0.5_metrics.png 就是 LSTM 模型在 lr0.001、b64batch_size64、h64hidden_size64、d0.5dropout0.5条件下的结果图。文件名本身就是一条超参数记录把关键配置写进图片名是记录实验的好习惯改模型时再也不用翻聊天记录确认配置。对比这组图可以看出在同样的超参数下CNNGRU 的收敛速度通常比纯 LSTM 快。理由前面提过一维卷积先提取了局部特征给循环层减了负训练时梯度传播路径更短。而纯 LSTM 在小规模数据上表现往往不如简单结构。我拆这份资源时最大的感受是这三个模型作为对比基线非常合适。先用四组默认模型各跑一遍记录指标到表格里你就有了自己数据集上的 baseline 结果之后任何改进方案都有了可比对象。5. 新手最容易踩的四个坑全零输出、指标很差、loss 不降、显存溢出5.1 预测结果是一条水平线或全零现象训练完成后预测曲线在图上就是一条直线或者全部趋近于零本来应该是起伏的曲线消失了。原因最常见的是数据预处理阶段出了问题。流量数据的数值范围往往很大——高峰时段上千夜间几乎为零——如果不做归一化直接喂给网络激活函数的输出会被大数值推到饱和区梯度消失模型学不到任何规律。还有一种情况是标签错误地取了未做处理的原始流量值导致 loss 在训练初期就异常巨大。解决重新走一遍数据预处理把训练集的流量值缩放到 [0, 1] 区间再训练。记住一个原则训练集和验证集、测试集要用同一套缩放参数也就是说你先在训练集上算出 min 和 max然后把这个 min/max 用到所有数据集上不要在验证集上重新计算一遍。这份代码里如果没显式做归一化你需要自己补上一段预处理。5.2 复现论文指标但结果总差一大截现象按照论文或博客里的指标RMSE 应该在几百的范围内但你的结果到了几千。原因很多公开代码在指标计算时偷偷做了处理。常见的有两种第一种是计算指标时把预测值反标准化回原始流量范围用真实流量量级去评估第二种是训练和评估用了不同长度的数据切片评估数据集更短。你如果没有做反标准化直接拿归一化之后的数值算 MAE数值自然小到不像话反过来你用了原始流量算又会大到离谱。解决先确认评估指标是在哪个数据域里计算的。如果模型输出的是归一化后的结果先把输出反归一化回真实流量再和真实值一起计算 RMSE这样对比论文指标才有意义。反归一化操作就是 x_real x_norm * (max - min) min。5.3 Loss 下降极慢甚至一开始就在高位徘徊现象训练了十几个 epochloss 就是稳稳地横在那里纹丝不动。原因这是学习率设置失配的典型症状。lr0.001 在多数情况下试用正常但如果你的数据量特别小梯度本身就很小0.001 的更新步长可能不够用。还有一种可能是权重初始化不太合适把梯度直接送进了梯度消失的区间。解决先试把学习率调到 0.01观察前三个 epoch 的 loss 是否明显下降如果是就说明原学习率太小。如果调到 0.01 后 loss 爆炸就回到 0.005 再试。这个范围内做二分查找最稳妥。同时检查一下输入数据的量级如果输入的流量数值特别大上万级别先做缩放处理否则梯度在反向传播时容易溢出。5.4 CUDA out of memory 或卡死现象train 一跑就报 CUDA out of memory或者 GPU 显存占用居高不下。原因batch_size 设得太大、序列过长、或隐层维度太高三者只会叠加。这个项目的默认参数是 64/64/0.5显存占用不大报 OOM 大概率是改了配置或者开了过多其它应用。解决把 batch_size 降到 32 再试。还不行就检查 PyTorch 的显存分配策略——训练完一次后调小 batch_size需要重启进程释放缓存这在 PyTorch 中很常见。用 nvidia-smi 看看显存是持续占满还是没有释放即可判断。6. 数据不换代码也能换把 LSTM 跑在自己的流量数据上6.1 换数据的最小改动只改 data_loader.py 和数据加载函数如果你想用自己的流量数据跑这套源码不需要改模型文件只需要修改数据加载的部分。先把任何格式的数据转成 numpy 数组形状为 (样本数,)然后复用 generate_dataset 函数生成序列样本。关键在最后封装 DataLoader 时的维度处理——模型期望的输入形状是 (batch_size, time_step, feature_dim)很多新手在这一步把维度搞反直接报 shape mismatch。# 自定义数据加载流程 import numpy as np import torch from torch.utils.data import DataLoader, TensorDataset # 假设你的数据是 csv一列流量值 raw_data np.loadtxt(my_flow_data.csv, delimiter,) x, y generate_dataset(raw_data, time_step12) x x.reshape((-1, 12, 1)) # 关键补上 feature 维度 y y.reshape((-1, 1)) dataset TensorDataset(torch.FloatTensor(x), torch.FloatTensor(y)) loader DataLoader(dataset, batch_size64, shuffleTrue)这里最容易踩的坑是 feature_dim 的放置顺序。PyTorch 的 Conv1d 和 LSTM 对输入维度的顺序约定不同LSTM 期望 (seq_len, batch, input_size) 或 (batch, seq_len, input_size)而 Conv1d 期望 (batch, channels, seq_len)。这份资源里的模型为了让 CNN 和 LSTM 串联大概率统一成了 batch 在前的格式。你自己改的时候先跑一个 batch 打印 shape确认无误再全量训练。6.2 时序交叉验证用混乱的切分方式换来虚高指标换数据后第二个容易犯的错误是 train/test 划分方式。横截面数据可以随机切分时序数据不行。流量数据存在明显的时间自相关性如果随机打乱再切分训练集里混入了测试集时间段附近的样本模型相当于提前看到了未来部分信息验证指标会虚高至少 10% 到 20%。# 正确的时序划分方式 split_ratio 0.8 split_index int(len(raw_data) * split_ratio) train_data raw_data[:split_index] # 前 80% 时间 test_data raw_data[split_index:] # 后 20% 时间注意用这个方式时在 test_data 上生成序列样本时你需要用到 train_data 末尾的最后 12 个数据点作为起始输入这在实际部署中是合理的因为真实场景里你预测未来时手上是持有最近 12 个历史点的。划分之后你会感叹验证曲线明显变真实了不再像之前那么好看。6.3 模型对比的条件控制换模型不换配置也给未来的自己留一手当你要正式对比 LSTM、GRU 和 CNNGRU 时务必保持相同的实验条件相同数据划分、相同 loss 函数、相同训练轮数、相同批次大小。只要有一个条件不一致对比结论就不成立。在这套源码里切换模型只改 configuration.py 里的 model 字段即可非常方便保持其它条件一致。我的习惯是每次实验把配置信息写进文件名沿用这套资源命名规则的思路只不过我会再多加一个时间戳。跑上几组实验后你会彻底明白一组超参数对不同模型的影响是非线性的适合 LSTM 的参数未必是 GRU 的最优参数。如果后续只打算部署一个模型我建议以验证集 RMSE 为准来选择模型不要太纠结训练速度。混合模型参数量大训练时间长在实时性要求高的场景下gru 单模型的性价比通常是最高的。这套项目跑完之后我再也没有用过一次性代码写流量预测实验。从那以后我每次新项目都会强制走一遍这套流程配置统一数据加载与模型定义分离评估指标固定实验配置写进文件名。这么做能帮我快速建立不同思路之间的可比性少走至少一个月弯路。希望帮到你。本文还有配套的精品资源点击获取
返回列表