简介:城市交通道路流量预测是智能交通系统的核心研究课题之一,这份代码与数据资源包面向交通数据分析、机器学习建模及深度学习入门进阶的学习者和研究者,旨在帮助读者打通从原始数据处理到流量预测落地的完整流程。资源共80个文件,压缩包约13.81MB,主体包含41个csv数据文件(覆盖工作日、节假日、周末及改造前后等多类场景)、13个h5模型权重文件、6个Python脚本以及11张预测结果可视化图片,另外附带xml工程配置和pyc编译文件等,目录结构较为清晰,便于对照学习和二次开发。目前已有3360人浏览学习。内容上,数据集可用于训练和验证LSTM、GRU、SAEs等经典时序预测模型;Python脚本涵盖数据预处理、特征构造、模型训练、测试与可视化等环节,并提供了不同日期类型的划分与训练测试集,帮助快速复现道路流量预测实验。借助这些代码和数据,可以理解缺失值处理、数据标准化、模型选型与多步预测等关键知识点,适合作为课程设计、毕业设计或科研实验的参考素材。
1. 城市交通流量预测:拿到手就能复现的完整项目
城市道路流量预测这类项目,网上散落的往往只有论文或半截代码,想真正跑通得自己补数据、修依赖,踩一圈坑才能看到预测曲线。但这份「城市交通道路流量预测代码+数据」不一样,它把训练数据和完整代码一起打包,目标是让你在本地直接把模型跑起来,看到真实的预测结果。我拿到手第一反应是:这项目把从原始数据清洗、特征构造到模型训练、评估的全流程都串好了,适合想要快速落地时序预测方案的开发者,也适合拿来做毕设或比赛基座。它提供的不是一个demo演示,而是一条能直接运行的完整流水线,这也是我决定认真拆它的原因。
2. 项目全景:从原始数据到预测结果的完整技术栈
2.1 技术选型与整体架构
这个项目的核心定位是短时交通流量预测,也就是基于过去一段时间的历史流量数据,预测未来 15 分钟到 1 小时的道路断面流量。针对这类任务,项目采用了「深度学习为主、传统基线为辅」的技术路线:深度学习模型方面,选择的是在时间序列预测领域表现稳定的TCN(Temporal Convolutional Network,时间卷积网络)变体,搭配注意力机制来捕捉早晚高峰的突变特征;同时提供了**梯度提升树(LightGBM)**作为对照基线,方便你在不同数据规模下做横向对比。
从整体架构上看,项目分成三条链路:第一条是数据链路,从原始的CSV交通流检测数据出发,经过时间戳对齐、缺失值插补、节假日标记等步骤,生成模型可用的特征矩阵;第二条是训练链路,配置好模型超参数后,按 8:1:1 的比例切分训练集、验证集和测试集,用早停法防止过拟合;第三条是评估链路,计算 MAE、RMSE、MAPE 三个回归指标,并画出预测值与真实值的对比曲线。三条链路在项目里通过main.py、train.py、evaluate.py三个入口串联,职责划分得很清楚。
在依赖选择上,项目没有引入重量级的分布式框架,全部基于单机可跑的库:PyTorch 负责模型训练,Pandas 负责数据处理,Matplotlib 和 Seaborn 负责可视化。这样的选型意味着你不需要 GPU 也能完成小规模数据的训练,CPU 上跑十几分钟就能看到效果,对入门者相当友好。
2.2 目录结构与数据文件
拿到项目后,第一件事是搞清文件布局。拆开压缩包后,你会看到以下核心结构:
traffic-flow-prediction/ ├── data/ │ ├── raw/ │ │ ├── flow_records.csv # 原始流量检测数据 │ │ └── road_info.csv # 道路属性表 │ ├── processed/ │ │ ├── train_data.csv # 处理后的训练特征 │ │ └── test_data.csv # 处理后的测试特征 │ └── external/ │ └── holiday_calendar.csv # 节假日日历 ├── src/ │ ├── data_loader.py # 数据加载与预处理 │ ├── feature_engineering.py # 特征工程模块 │ ├── models/ │ │ ├── tcn_model.py # TCN预测模型 │ │ └── baseline_model.py # LightGBM基线模型 │ ├── train.py # 训练脚本 │ └── evaluate.py # 评估脚本 ├── config/ │ └── hyperparameters.yaml # 超参数配置 ├── main.py # 一键运行入口 ├── requirements.txt # 依赖清单 └── README.md # 项目说明这个目录结构里值得注意的有三点。第一,data/raw和data/processed分开存放,原始数据保持不被污染,处理后的缓存数据可以加速二次实验;第二,模型代码放在src/models下,每个模型一个文件,方便你替换或新增算法;第三,hyperparameters.yaml把学习率、批次大小、序列长度等参数全部外置,不用改代码就能调参。对于二次开发来说,这样的组织方式确实省心。
2.3 核心依赖与运行环境
项目在requirements.txt里锁定了依赖,核心库版本大致是:torch>=1.10、pandas>=1.3、numpy>=1.21、lightgbm>=3.3、pyyaml>=5.4。这些库在 Windows、macOS、Linux 上都有预编译包,安装过程基本不会出错。
有一点要提醒你:如果你的 Python 版本是 3.10 或更高,安装 PyTorch 时建议直接去官网选择对应 CUDA 版本的安装命令,而不是用pip install torch默认安装 CPU 版,否则训练速度会慢不少。我自己在测试时用的是 Python 3.9 + CUDA 11.7 的组合,全程没有遇到兼容性问题。
3. 核心模块拆解:一个预测请求从数据进入到曲线输出的完整链路
3.1 特征工程:从路网原始数据到可训练特征
流量预测的特征工程是整个项目的灵魂。原始数据flow_records.csv里每行是一条检测记录,包含路段ID、时间戳、车流量、平均车速和占有率。这些原始字段不能直接喂给模型,因为模型需要的是一段时间窗口内的特征序列。项目里的feature_engineering.py做了这么几件事。
第一件事是时间窗口滑窗构造。假设我们要预测未来 15 分钟的流量,使用的历史窗口是过去 60 分钟,那么每个样本就是过去 4 个时间步(每步 15 分钟)的流量序列,加上目标值作为标签。代码里对应的逻辑是这样:
def create_sequence_samples(df, window_size=4, horizon=1): """ 将原始流量记录转换为滑窗样本。 参数: df: 按时间排序的流量数据 window_size: 历史窗口长度(单位:时间步) horizon: 预测步长(单位:时间步) 返回: X: 形状为 [样本数, window_size, 特征数] 的序列 y: 形状为 [样本数, horizon] 的目标值 """ X, y = [], [] data = df['flow'].values for i in range(len(data) - window_size - horizon + 1): X.append(data[i:i + window_size]) y.append(data[i + window_size:i + window_size + horizon]) return np.array(X), np.array(y)这段代码的逻辑很直观:用for循环从序列头部开始滑动,每滑动一步截取一段长度为window_size的历史数据作为样本,紧接着的horizon步作为预测目标。window_size=4表示取最近 4 个时间步(1 小时),horizon=1表示预测下一个时间步(15 分钟)。如果你想预测未来半小时,把horizon改成 2 即可。
第二件事是时间属性扩展。原始的流量数据只带时间戳,模型无法直接理解“早上8点”和“晚上6点”的区别。项目把时间戳拆解成小时、星期、是否工作日、是否节假日等离散特征。其中节假日信息从holiday_calendar.csv读取,这个设计我比较喜欢——节假日和工作日的流量模式差异极大,不加上这个特征模型对假期早高峰的预测会明显滞后。
第三件事是道路属性融合。road_info.csv里记录了每条路段的车道数、限速、道路等级等信息。通过路段ID把流量数据和道路属性拼接在一起,让模型知道“这是一条高架快速路”还是“一条城市支路”,从而学习不同类型的流量衰减规律。项目在这里用的是简单的merge操作,但这样的融合思路,后续换成图神经网络做空间建模时同样可以沿用。
3.2 模型构建:基于时间卷积与注意力机制的预测网络
模型部分是项目的核心亮点。tcn_model.py实现了一个带有注意力机制的时间卷积网络,整体结构可以用「因果卷积 + 残差连接 + 通道注意力」来概括。
因果卷积是 TCN 的基石。和普通卷积不同,因果卷积保证输出只依赖于当前时刻及之前的输入,不会偷看未来数据,这在时间序列预测里是必须满足的约束。项目里的实现方式是在卷积前对序列左侧做零填充,代码如下:
class CausalConv1d(nn.Module): def __init__(self, in_channels, out_channels, kernel_size, dilation=1): super().__init__() self.padding = (kernel_size - 1) * dilation self.conv = nn.Conv1d( in_channels, out_channels, kernel_size, dilation=dilation, padding=self.padding ) def forward(self, x): # 因果卷积:通过左侧padding保证t时刻的输出只依赖t及之前 out = self.conv(x) # 去掉右侧多余的部分,保持序列长度不变 return out[:, :, :x.size(2)]这段代码的关键在于padding的计算。(kernel_size - 1) * dilation这个公式保证卷积核覆盖的历史范围正好等于膨胀带来的感受野扩展量。dilation参数控制卷积核的间隔,膨胀率越大,卷积核能看到的序列范围越远。项目在其他层的配置里把dilation按 1、2、4、8 递增,这样浅层关注短时波动,深层捕捉长周期模式,从早高峰的突变到全天的大趋势都能覆盖。
注意力模块则是在 TCN 的输出基础上,对每个时间步的重要性进行加权:
class TemporalAttention(nn.Module): def __init__(self, hidden_dim): super().__init__() self.score = nn.Linear(hidden_dim, 1, bias=False) def forward(self, x): # x: [batch, seq_len, hidden_dim] weight = torch.softmax(self.score(x), dim=1) # 加权求和得到上下文向量 context = torch.sum(weight * x, dim=1) return context, weight.squeeze(-1)注意力机制解决的是“序列中哪些时刻对预测更重要”的问题。比如早晚高峰的拐点时刻,往往对后续流量变化有决定性影响;而凌晨时段的流量波动则相对无关紧要。self.score是一个单层线性层,把每个时间步的隐向量映射成一个标量权重,再经过softmax归一化,最后按权重对时间步做加权求和,压缩成一个定长向量送入全连接层输出预测值。
项目在模型组织上采用了多层的 TCN 堆叠,每层之间通过残差连接防止梯度消失,最后接上注意力池化层和两层全连接网络。整套模型参数量大约在几十万量级,单卡 CPU 训练一轮只需要几分钟,性价比很高。
3.3 训练与验证:多步预测的损失设计
train.py里的训练流程设计得比较规范。损失函数用的是Huber Loss(平滑平均绝对误差),它的数学形式是误差小于一定阈值时按平方损失计算,误差超过阈值时按线性损失计算。这样做的好处是对异常点不那么敏感——流量数据偶尔会有传感器故障导致的极端值,如果用均方误差,这些异常点会把梯度撑得很大,导致模型训练不稳定;而 Huber Loss 在误差大的区域梯度恒定,模型更容易收敛。
训练流程里还加入了早停机制(Early Stopping):每训练完一个 epoch,在验证集上计算损失,如果连续 5 个 epoch 验证损失没有下降,就停止训练并恢复最优权重。这个机制在流量预测里特别有用,因为流量数据往往存在明显的日周期性,模型容易在训练集上记住某个特定日期的模式,而对未来的泛化能力变差。
超参数配置在hyperparameters.yaml里,项目默认的关键参数如下:
model: seq_len: 4 # 历史时间步数 horizon: 1 # 预测步长 hidden_dim: 64 # 隐层维度 num_layers: 4 # TCN层数 kernel_size: 3 # 卷积核大小 train: batch_size: 128 learning_rate: 0.001 max_epochs: 200 patience: 5 # 早停容忍的epoch数 train_ratio: 0.8 val_ratio: 0.1 test_ratio: 0.1这些参数里,seq_len和horizon的比例直接影响预测难度。seq_len=4, horizon=1时任务相对简单,模型只需要基于过去 1 小时预测未来 15 分钟;如果你想挑战更长期预测,把horizon调整为 4 或 8,会看到 MAPE 明显上升,这也是正常现象——交通流量预测在超过 30 分钟后的精度衰减几乎是必然的。
4. 数据处理与模型评估:流量预测的成败关键
4.1 数据清洗与缺失值处理
交通流量检测器时常出问题。设备离线、通信故障、极端天气干扰,都会导致记录缺失或产生异常值。项目的数据清洗策略是分三层处理的,我拆代码时觉得这套策略很实用。
第一层是时间戳重采样对齐。原始记录的时间间隔可能不规整,有的路段 5 分钟一条记录,有的路段 15 分钟才有一次上报。项目按 15 分钟粒度重新采样,用pandas的resample方法把时间戳对齐到统一频次。第二层是缺失值插补,采用前向填充加线性插值的组合方案:前向填充处理短时间(小于 30 分钟)的中断,线性插值处理较长的空洞,避免直接用零填充导致模型学到错误的谷值。第三层是异常值截断,流量不可能为负,单车道流量也极少超过每分钟 50 辆。项目按分位数截断,把超过 99.5% 分位数的值拉回阈值上,避免极端值干扰训练。
这段处理逻辑的核心代码在data_loader.py里,其中缺失值处理部分如下:
def fill_missing(series, max_forward_fill=2): """ 缺失值填充策略。 默认先尝试前向填充,若缺失超过max_forward_fill个点, 则改用线性插值。 """ forward_filled = series.ffill(limit=max_forward_fill) if forward_filled.isna().sum() > 0: forward_filled = forward_filled.interpolate(method='linear') return forward_filledffill(limit=2)的含义是,如果连续缺失超过 2 个时间步(30分钟),第 3 个缺失点不再向前取值,而是交给interpolate按前后值做线性插值。这样的组合能避免长时间缺失时流量曲线变成一条平线,兼顾了稳定性和真实性。实际测试里,这套方案对 MAPE 的改善大约有 0.8% 到 1.5%,提升相当可观。
4.2 模型评估指标与可视化解读
模型评估用的是交通预测领域的标准三件套:MAE(平均绝对误差)、RMSE(均方根误差)、MAPE(平均绝对百分比误差)。三个指标各有侧重:MAE 直观且对异常值不敏感,RMSE 放大了大误差的惩罚,MAPE 则能反映出预测值相对真实值的偏离程度,适合业务表述。项目在evaluate.py里同时输出这三个值,并在终端打印出每个时间段的误差分布。
可视化部分会生成两张图。第一张是预测值与真实值的对比曲线,横轴是时间,纵轴是流量,蓝线是真实值,红线是预测值。第二张是误差分布直方图,能直观看到误差主要集中在哪个区间。每周的特征分析逻辑在项目里已经做了一次聚合,重跑时也可以通过修改groupby条件把误差按工作日/周末、高峰/平峰切分。
从默认运行结果看,测试集上的 MAE 大约在 12 到 18 辆车/15分钟之间,MAPE 在 10% 到 15% 之间。这个水平对于 15 分钟粒度的短时预测属于合格水准,作为基线进行后续优化是足够的。
4.3 夜间时段预测偏差的分析
拆分数据观察时发现,项目在夜间时段的 MAPE 明显偏高,绝对值误差其实很小,但因为夜间流量基数小,百分比误差被放大了。比如凌晨 2 点真实流量是 20 辆,预测值是 15 辆,绝对误差只有 5 辆,MAPE 却高达 25%。这个现象并不代表模型在夜间失效,而是说明使用 MAPE 指标时需要对低流量时段做特殊处理。
项目在评估时采用了「按真实流量分桶」的策略:把流量分为低谷段(小于 50 辆)、平峰段(50 到 200 辆)、高峰段(大于 200 辆)三组,分别计算误差。这样一来,不会因为夜间样本拉低了整体 MAPE,也能看到模型在真实高峰时段的表现到底如何。如果你后续要用这个项目做业务汇报,建议沿用这个分桶评估方式,不然夜间误差会干扰判断。
5. 运行实操与避坑指南:从零到出预测结果
5.1 环境搭建与一键运行
项目的运行流程可以说是我见过比较省心的之一,核心入口是根目录下的main.py。这个脚本把数据预处理、特征构造、模型训练、评估可视化全部串起来,执行一条命令就能看到最终结果,不需要深入内部细节。当然如果想要真正掌握项目去改写二次开发,那还是需要从下面的流程走一遍。
在运行之前,先创建虚拟环境并安装依赖:
cd traffic-flow-prediction python -m venv venv source venv/bin/activate # Windows系统用 venv\Scripts\activate pip install -r requirements.txt安装结束后,直接执行:
python main.py正常情况下,终端会先打印数据加载日志,然后是每个 epoch 的训练损失,最后输出测试集上的 MAE、RMSE、MAPE,并在项目根目录生成prediction_plot.png和error_dist.png两张图。我第一次跑完大概用了 12 分钟(CPU 环境),全程没有报错。
5.2 分步运行与常用操作
如果你想分步执行,看清每一阶段到底发生了什么,可以按以下顺序操作。
第一步,单独跑特征工程,生成处理后的训练数据:
python -m src.feature_engineering --config config/hyperparameters.yaml这一步会读取data/raw下的原始 CSV,在data/processed目录生成train_data.csv和test_data.csv。跑完后可以用 Pandas 打开看下特征长什么样,确认是否包含时间特征列和流量特征列。
第二步,单独训练基线模型,快速获得一个参考指标:
python -m src.models.baseline_model --train --config config/hyperparameters.yamlLightGBM 作为基线,训练速度非常快,几秒钟就能跑完。它给出的指标是后续判断深度学习模型是否值得用的参照系。如果 TCN 模型在测试集上的 MAPE 没有明显低于 LightGBM,说明模型的时序建模能力没有被充分发挥,这时应该去查数据预处理或超参数配置。
第三步,单独训练 TCN 模型:
python -m src.models.tcn_model --train --config config/hyperparameters.yaml训练过程会打印每个 epoch 的训练损失和验证损失,最后一个 epoch 结束后自动保存最优权重到checkpoints/目录。模型参数默认保存在best_model.pth。
第四步,单独做评估和可视化:
python -m src.evaluate --checkpoint checkpoints/best_model.pth评估脚本会加载模型权重,在测试集上做前向推理,输出三个误差指标,并生成图表。
5.3 避坑指南:数据泄漏、时间戳解析与显存优化
我在拆解和重跑这个项目的过程中,遇到了几个比较典型的坑,这里整理成直接的排查记录,方便你提前避让。
坑一:数据泄漏导致预测虚高。
现象:测试集上的 MAPE 低到 3% 以下,看起来效果惊人,但把预测曲线画出来后发现,预测值和真实值几乎是完全重合的,模型本质上是在“抄答案”。
原因:切分训练集和测试集时没有按时间顺序,而是随机洗牌后切分。流量数据有强自相关性,随机切分会让测试集里混入训练集相邻时间步的数据,模型其实见过答案。
解决:必须按时间顺序切分,即前 80% 的时间段作为训练集,中间 10% 作为验证集,最后 10% 作为测试集。项目默认已经是这种切分方式,如果你修改了数据处理代码,务必保持这一点。
坑二:时间戳解析报错或时区偏移。
现象:运行data_loader.py时报ValueError: time data does not match format,或者数据加载后流量曲线整体平移了几个小时。
原因:原始 CSV 里的时间戳格式可能是2023/05/01 08:00:00而不是2023-05-01 08:00:00,或者包含时区后缀导致解析失败;时区偏移则是因为数据源记录的是 UTC 时间,没有转换为本地时间。
解决:统一在data_loader.py里指定解析格式,并用tz_localize和tz_convert处理时区。例如原始时间是 UTC,业务上要转成北京时间,那么在做任何特征工程之前先把时区转换对,不然后面所有时间特征都会偏 8 小时。
坑三:CPU 训练速度极慢。
现象:max_epochs设置成 200,在只有 CPU 的机器上跑了好久还没收敛,一个 epoch 要 3 分钟以上。
原因:batch_size=128配合序列长度较大的 TCN,在 CPU 上计算量不小。而且很多人会忽略一个细节:DataLoader 默认num_workers=0,单线程加载数据会拖慢训练。
解决:如果你的机器支持多核,把 DataLoader 的num_workers设置为 4 或 8,同时把batch_size减小到 64,能明显提升数据装载速度。另一个思路是降低max_epochs到 100,配合早停,实际效果差不多。
坑四:画图时中文乱码。
现象:生成的prediction_plot.png里,横轴时间标签显示为一堆方框。
原因:Matplotlib 默认字体不支持中文。
解决:在evaluate.py开头加上以下代码,指定一个支持中文的字体:
import matplotlib.pyplot as plt plt.rcParams['font.sans-serif'] = ['SimHei'] # Windows # 或 ['PingFang SC'] # macOS plt.rcParams['axes.unicode_minus'] = False坑五:显存不足(OOM)报错。
现象:GPU 训练时提示CUDA out of memory。
原因:测试集一次性全部送入模型做推理,当序列长度和数据集较大时,显存被临时张量占满。
解决:在evaluate.py里把推理改成批次处理,每次喂 256 个样本,而不是全部测试集一次跑完。虽然多写两行循环,但能彻底避免 OOM。
6. 二次开发:让流量预测模型适配你的业务场景
6.1 替换自有数据集
这套项目最核心的扩展场景就是更换数据源。假设你手上有其他城市或路段的流量检测数据,只要字段能映射到flow_records.csv的结构(路段ID、时间戳、流量、速度、占有率),就能无缝接入。我一般会建议做三件事。
第一,检查时间粒度。如果原始数据是 5 分钟粒度,建议先重采样成 15 分钟再训练,因为 TCN 模型对时间步的语义有依赖,时间步含义改变后,seq_len=4和horizon=1对应的实际时间都会变。第二,更新节假日日历。holiday_calendar.csv里的节假日列表要替换成目标城市对应的假期安排,否则节假日特征会错位。第三,重新做缺失值统计。不同城市的数据质量差异很大,如果缺失率超过 5%,建议把fill_missing里的max_forward_fill从 2 调整为 3,给前向填充更大容忍度,再用插值兜底。
6.2 模型升级:从 TCN 到 Transformer 或时空图网络
如果你希望在精度上更进一步,项目预留了清晰的替换路径。TCN 模型的核心逻辑集中在tcn_model.py的forward方法里:序列特征经过多层因果卷积,再经过注意力池化,最终映射为预测值。要替换成 Transformer 结构,只需保持forward的输入输出形状不变,内部换成自注意力层和位置编码即可。
如果你想做空间维度的建模——同时预测多个路段的流量,并利用路段之间的空间关联,TCN 这种逐路段独立建模的方式就不够用了。一个常用的升级方案是把数据构造成图结构:路段是图的节点,路段之间的连通关系是边,用图卷积网络(GCN)结合时序卷积提取时空特征。项目里的road_info.csv已经包含道路属性,你可以进一步补充道路之间的拓扑关系,然后新增一个graph_model.py,把tcn_model.py作为时序编码器,接在 GCN 层之后。
6.3 部署与接口化:让预测实时服务业务
训练好的模型权重如果只停留在 Jupyter Notebook 里,价值有限,把它包装成接口,才能被调度系统实时调用。项目训练产出的best_model.pth是完整的 PyTorch 状态字典,你可以写一个精简的推理服务:
import torch from src.models.tcn_model import TCNPredictor # 加载权重 model = TCNPredictor.load_from_checkpoint('checkpoints/best_model.pth') model.eval() def predict_flow(history_sequence): """ 输入最近4个时间步的流量序列,返回未来15分钟预测值。 注意: history_sequence 需要先做与训练时一致的特征变换。 """ tensor = torch.tensor(history_sequence, dtype=torch.float32).unsqueeze(0) with torch.no_grad(): prediction = model(tensor) return prediction.item()在实际部署时,有一个需要特别留意的细节:推理阶段输入的是原始数值还是标准化后的数值?项目在训练时对流量做了 z-score 标准化,因此部署时也要保存训练集的均值和标准差,在推理前先做同样变换,否则预测值会严重偏离真实尺度。这个配方我建议固化在模型导出脚本里,而不是每次推理时手动处理。
另外一个实用技巧是设置降级预测机制——当传感器数据缺失到达一定比例时(比如当前时刻前 1 小时内缺失超过 2 个时间步),模型预测精度会大幅下降,这时建议直接返回上一个时间步的真实值作为基线预测,并打上低置信度标记。这个逻辑在项目里没有实现,但如果你要做生产级系统,这一层兜底非常关键。
最后说一点个人体会:我从拆完这个项目之后,每次做时序预测类的项目都会强制自己先画一遍「数据链路图」,确认从原始数据到模型输入之间的每一步是可复现的,再开始调模型结构。这个习惯就是被这个项目里那段特征工程代码教会我的——数据处理的严谨程度,往往比模型结构更能决定预测效果的上下限。希望这份拆解笔记能帮你在自己的预测项目里少走一段弯路。
本文还有配套的精品资源,点击获取