简介这是一份面向人工智能课程设计与期末大作业场景的纽约出租车流量预测Python项目基于深度学习完成数据加载、模型训练与可视化分析。实现包含GRU、LSTM、CNN-LSTM、CNN-GRU四种模型代码注释较完整配合预处理的npz数据集与docx数据说明新手按说明配置环境即可运行。资源包共31个文件约1.21MB主要包括9个Python脚本、2个numpy数据文件、1份docx数据说明、3张训练指标图及配置文件等结构清晰便于按模块阅读和修改。目前已有355人学习下载。通过运行main.py可完成四种模型的训练与指标对比还能直接查看输出的损失与评估图适合作为深度学习入门实战、课程报告或答辩项目的参考。1. 纽约出租车流量预测这份深度学习大作业为何值得照着做一遍做深度学习课设最怕什么不是模型跑不起来而是数据集又大又脏预处理写到崩溃最后交上去的代码还说不清楚。这份基于深度学习的纽约出租车流量预测项目恰好绕开了这个坑数据已经整理成 npz 格式代码按模块拆好连损失曲线和指标图都一并给出属于那种「下载下来、简单部署、就能出结果」的高分大作业。对于正在选人工智能大作业题目的学生来说它最大的价值不是模型多新而是把一条完整的实验链路摆在你面前——数据加载、模型定义、超参数配置、训练可视化全都有代码可循。我拆完这套资源后最直接的感受是它的代码结构适合照着改成自己的课设无论是换数据集还是换模型都有清晰的落点可改。下面按实际使用顺序把每个文件的作用、参数含义和踩坑点讲清楚。2. 先读懂数据npz 文件结构与 data_loader.py 的加载方式2.1 数据长什么样volume_train.npz 与 volume_test.npz 的读取方式项目的数据集中在 NYC-stdn 目录下volume_train.npz 和 volume_test.npz 都是 numpy 压缩格式。第一次拿到手先不要急着训练用下面这段代码确认数据规模和格式import numpy as np data np.load(NYC-stdn/volume_train.npz) print(data.files) # 查看 npz 里有哪些键 train_volume data[volume] # 常见键名是 volume test_data np.load(NYC-stdn/volume_test.npz) test_volume test_data[volume] print(train_volume.shape, test_volume.shape)这段代码做的事很简单np.load 读入压缩包files 属性列出内部所有键名再用键名取出流量矩阵。实际打印出来会发现维度是三维的一般结构是(时间段数量, 网格区域数量, 特征维度)或者(样本数, 时间步, 节点数)具体以 data_loader.py 里的 reshape 逻辑为准。纽约出租车流量预测这类任务最常见的处理方法是把城市划分成网格每个网格统计一段时间内的上下车客流再把连续时间段组成一个时间序列。项目里既然已经把数据整理成 npz说明网格划分和时间聚合这一步已经做完你拿到的是可以直接喂进模型的张量。2.2 data_loader.py 的加载逻辑时间窗口与批次划分data_loader.py 这个文件承担了从原始数据到 PyTorch Tensor 的转换。拆项目时我一般会先看它的函数签名再沿着数据流向走一遍。常见的实现方式是这样的import numpy as np import torch from torch.utils.data import Dataset class TrafficDataset(Dataset): def __init__(self, data, input_size12, output_size1): self.data torch.FloatTensor(data) self.input_size input_size self.output_size output_size def __len__(self): return len(self.data) - self.input_size - self.output_size 1 def __getitem__(self, idx): x self.data[idx: idx self.input_size] y self.data[idx self.input_size: idx self.input_size self.output_size] return x, y这里 input_size 表示用过去多少个时间段作为输入output_size 表示预测未来多少个时间段典型取值是 input_size12、output_size1也就是用过去 12 个时间片预测下 1 个时间片。构造 PyTorch Dataset 时需要注意getitem返回的 x 和 y 都保留了时间维度x 的 shape 是(input_size, 节点数, 特征数)y 是(output_size, 节点数, 特征数)。后续模型里 LSTM 或 GRU 的输入要求是(seq_len, batch_size, feature_dim)所以在训练代码里通常还需要做一次 permute 调整维度顺序。2.3 时间序列的数据划分不能随机打乱很多第一次接触流量预测任务的人会在划分训练集和验证集时直接调用 train_test_split这就是典型翻车现场。交通流量是强时间相关数据昨天下午 5 点的晚高峰和今天下午 5 点的晚高峰高度相似如果验证集随机从整个时间轴里抽取就会出现「模型见过验证集附近数据」的数据泄露问题最终指标虚高答辩时说不清楚。正确做法是按时序截断前面 80% 的时间段作为训练集后面 20% 作为验证集保证验证集时间严格晚于训练集。我在实际拆这个项目时也确认了这一点main.py 里如果没有显式随机打乱说明作者保留了这种时序划分方式这是这类项目里正确的处理姿势。3. 配置与模型configuration.py 里的超参数和四套可选网络3.1 超参数集中管理configuration.py 里该关注哪些字段configuration.py 的价值在深度学习大作业里经常被低估。直接写在 main.py 里的超参数每改一次都要翻半天文件集中到一个配置文件里调参效率提升明显。常见字段包括学习率、批次大小、隐藏层维度、Dropout 比率、训练轮数、模型选择、数据路径等config { learning_rate: 0.001, batch_size: 64, hidden_size: 64, num_epochs: 100, dropout: 0.5, model_name: cnn_gru, # 可选 lstm, gru, cnn_lstm, cnn_gru data_path: NYC-stdn/volume_train.npz, input_size: 12, output_size: 1, device: cuda }这里的每个参数都会直接影响结果学习率 0.001 是 Adam 优化器的常见起点batch_size 64 在中等规模流量数据集上兼顾了收敛速度和显存占用hidden_size 64 对新课设来说够用Dropout 0.5 是序列模型里防过拟合的经典取值。看到项目生成的图片文件名里有 lr0.001、b64、h64、d0.5 这样的标记就知道配置和实验是对应着的这种命名习惯也值得新人在自己做实验时模仿。3.2 四套模型分别解决什么问题LSTM、GRU、CNNLSTM、CNNGRUmodel 目录下四个文件对应四种网络结构这种设计对课程作业非常友好。LSTM 是长短期记忆网络适合捕捉流量数据里的周期性规律GRU 是 LSTM 的简化版参数更少、训练更快在小数据集上往往效果不输 LSTM。CNNLSTM 的思路是先用卷积层提取相邻区域之间的空间相关性——因为距离近的网格流量互相影响再用 LSTM 建模时间依赖。CNNGRU 同理只是把时序模块换成更轻量的 GRU。从课设角度看你不需要四个模型全都跑完只需要回答一个问题为什么选某个模型作为最终方案。比如你选了 CNNGRU理由是它同时考虑了空间邻接性和时间周期性参数规模又小于 CNNLSTM训练时间可控这已经是能拿高分的答辩逻辑。项目里四个模型代码都给了意味着你可以跑一个对比实验用表格呈现四种模型的验证集误差这就是很有说服力的实验部分。3.3 训练流水线main.py 如何把配置、数据、模型串起来main.py 是这个项目的总调度。配置由 configuration.py 管理数据由 data_loader.py 提供模型从 model 目录导入训练循环放在这里或者 func.py 里。一个典型的训练流程如下from configuration import config from data_loader import load_data, create_dataloader from model.gru import GRUModel import torch import torch.nn as nn from torch.utils.tensorboard import SummaryWriter train_loader, val_loader create_dataloader(config) model GRUModel(config[hidden_size], config[dropout]).to(config[device]) criterion nn.MSELoss() optimizer torch.optim.Adam(model.parameters(), lrconfig[learning_rate]) for epoch in range(config[num_epochs]): model.train() train_loss 0.0 for x_batch, y_batch in train_loader: x_batch x_batch.permute(1, 0, 2).to(config[device]) y_batch y_batch.squeeze(-1).to(config[device]) optimizer.zero_grad() output model(x_batch) loss criterion(output, y_batch) loss.backward() optimizer.step() train_loss loss.item() print(fEpoch {epoch1}, Loss: {train_loss / len(train_loader):.6f})permute 这一步把批次维度换到第二位满足 PyTorch 循环神经网络的(seq_len, batch, features)要求。损失函数用 MSELoss 是因为流量预测本质上是回归任务均方误差对较大误差更敏感能让模型更专注于峰值时刻的预测。如果后期想换思路改成 MAE 或 Huber Loss 也可以但课设阶段 MSE 是最省事也最容易解释的选择。4. 训练到出图用 draw.py 与 metrics 复现整条实验链路4.1 可视化文件里隐藏的命名信息images 目录下列出了四个模型训练后的结果图文件名本身就是一种信息cnngru_lr0.001_b64_h64_d0.5_metrics.png 表示用的是 CNNGRU 模型学习率 0.001batch_size 64hidden_size 64dropout 0.5。这个命名规则对课设尤其重要期末作业里往往要贴训练曲线图如果所有实验图都叫 figure1.png复盘时根本分不清哪张对应哪组参数。项目采用的这种命名方式可以直接借鉴模型名关键超参数内容描述一眼就知道这张图是什么条件下的产物。4.2 draw.py 能画什么损失曲线和预测值对比draw.py 负责把训练过程变成可视化的证据。常见功能包括两类一是训练集和验证集的损失曲线横轴是 epoch纵轴是 loss用来判断模型是否收敛、是否过拟合二是预测值和真实值的对比折线图一般选取验证集末尾一段连续时间段把模型输出和真实流量画在同一张图上直观展示拟合质量。如果你拿到代码后发现 draw.py 在画图前没有调字体直接跑可能中文乱码加一段 matplotlib 的字体设置就能解决。4.3 用整理成表格的方式看四个模型的差异训练完成后你会发现四张 metrics 图的数值都对应写在文件名里。整理实验结论时可以用这样一个简单格式模型RMSE训练耗时波动情况LSTM略高中等收敛平稳GRU与LSTM接近较短收敛较快CNNLSTM较低较长前期波动明显CNNGRU最低中等整体稳定这个表不需要精确数字目的只是展示对比逻辑。实际上 CNN 分支提取空间特征之后序列模型的输入维度下降了时间建模压力变小了RMSE 通常比纯序列模型低。但这不是绝对的数据量小的时候 CNN 反而可能带来额外过拟合风险所以要靠验证集指标来判定。4.4 复现时改哪些配置能立刻看到效果跑通之后想验证自己是不是真的理解了项目试着改三个地方。第一个是 configuration.py 里的 hidden_size从 64 改成 128观察训练时间和验证集误差的变化第二个是 dropout从 0.5 改成 0.3看模型是否更快过拟合第三个是 input_size从 12 改成 6看短窗口对预测精度的影响。这三个改动都不涉及模型结构本身但能帮助你快速建立「超参数变化 → 指标变化」的直觉答辩时被问到调参经历也有真实内容可讲。5. 避坑与常见问题五个真实踩坑记录5.1 加载 npz 报错提示路径不存在或 KeyError现象是运行 data_loader.py 报 FileNotFoundError或者出现了 KeyError: volume。原因是相对路径写得不严谨文件不在当前工作目录下或者 npz 文件内部的键名并非 volume。解决方式是先打印 data.files 查看实际键名再把 data_path 改为绝对路径拼接例如 os.path.join(os.path.dirname(file), NYC-stdn, volume_train.npz)。我拆这类资源时习惯先跑一段两行的验证脚本确认文件能打开再看后续。5.2 数据归一化写错位置导致验证集指标虚高现象是训练 loss 下降正常验证集 RMSE 非常低低到不真实甚至比训练集还低。原因是把 MinMaxScaler 对整个数据集 fit 了一遍验证集的信息在归一化阶段就进入了模型视野属于典型的数据泄露。正确做法是只用训练集的均值和极值对验证集做变换验证集不进 fit。Put differently如果你发现 test 曲线平滑得像训练曲线一样先检查归一化的 fit 范围。5.3 换用 CNNLSTM 之后报维度对不上现象是报错信息里出现 Expected 3D input, got 2D 或者 mat1 and mat2 shapes cannot be multiplied。原因是 CNN 输出的形状是(batch_size, channels, seq_len, nodes)而 LSTM 期望(seq_len, batch_size, input_size)中间缺失了手动 reshape。解决方式是在进入 LSTM 前用 permute 和 view 把张量调整为正确的维度顺序。这一条几乎是每个做时空混合模型的人都会遇到一次的坑所以不要慌仔细检查每一层输出形状就行。5.4 预测曲线像是真实曲线向右平移了一个时段现象是模型收敛了但预测值总是滞后真实值一个时间步高峰时段偏移明显。原因是回归任务惯用 MSE 损失模型学到的最优策略是输出前一时刻的值因为相邻时刻流量相近这样 loss 已经很小。解决方式有三种增加输入序列长度、在损失函数中加大峰值附近的权重、或者改用分类思路把流量分桶。课设阶段最简单有效的是增加 input_size让模型看到更长时间段减少对最近时刻的依赖。5.5 训练曲线震荡不收敛loss 上下跳动现象是 loss 前 20 轮下降之后开始上下波动甚至有上升趋势。原因是学习率偏大或 batch_size 偏小。0.001 在多数场景下没问题但数据分布差异大时不稳定可以把学习率降到 0.0005或者把 batch_size 翻倍到 128让每个 batch 的梯度方向更稳定。如果仍然不收敛检查数据是否存在 NaN 值用 np.isnan(data).any() 扫一遍即可。6. 把课设往上做的进阶用法反归一化验证、消融实验与空间可视化前面几章已经把项目跑通的完整路径讲完了。如果还有余力把下面这三个动作做掉作业质量能从「跑通」提升到「能讲」。第一个动作是反归一化验证。训练时为了加速收敛会对流量数据做 MinMax 归一化模型的输出是归一化空间里的数值直接拿去画图和真实流量对比没有意义。正确做法是在 draw.py 中保存 scaler 之后调用 inverse_transform 把预测值还原成实际流量单位再和真实值比较。这一步在答辩时非常加分因为评审老师看到的是「每半小时载客量」这种有物理含义的数值而不是 0 到 1 之间的抽象量。你需要在项目里确认 scaler 是否在训练代码中被保存到文件如果没有自己加一行 torch.save(scaler, scaler.pt) 其实很容易。第二个动作是消融实验。项目提供了四套模型如果时间充足把四套模型的验证集 RMSE 都跑出来做成柱状图重点讨论「CNN 分支到底有没有贡献」。做法是先跑纯 LSTM再跑 CNNLSTM两张图的对比就能说明空间特征提取的作用。如果结果不理想比如 CNNLSTM 没有显著优于 LSTM也可以从数据角度解释——当前网格划分粒度下空间相关性不明显这个结论同样是有效的实验产出。第三个动作是网格热力图可视化。流量预测不只有均方误差这一个视角把某一天的预测值按网格坐标排列成二维矩阵用颜色深浅表示流量高低会发现模型在早晚高峰时段把高流量区域预测得比较集中。这个可视化不需要额外模型只要你知道每个网格的行列索引把模型输出的向量 reshape 回(rows, cols)即可。热力图比折线图更有空间冲击力也更容易让答辩老师在 30 秒内理解你的模型学到了什么。以上三个动作做下来这份大作业就几乎覆盖了一篇完整实验论文的要素问题定义、数据说明、基线对比、消融验证、可视化分析。从那以后我每次打开这类课设项目都会先跑一遍完整的训练流程再强制自己完成一遍反归一化和热力图验证确认预测结果在真实尺度下是合理的。希望帮到你。本文还有配套的精品资源点击获取