纽约出租车流量预测大作业:从数据到模型全流程拆解
简介:这份资源面向人工智能、深度学习方向的课程学习者,提供一套完整的纽约出租车流量预测项目方案,适合作为期末大作业、课程设计或入门实战练习。项目以Python实现,围绕出租车流量时序数据展开建模与预测,涵盖数据处理、模型构建、训练与结果可视化等环节,代码附有注释,新手也能较快理解整体流程。压缩包共31个文件,约1.21MB,包含9个py源码文件、7个pyc缓存、6个xml配置、3张png结果图、2个npz数据集以及docx说明文档和md说明等,源码中可见cnn、lstm、gru、cnn_gru、cnn_lstm等多种网络结构,并配有训练指标曲线图,便于对比不同模型效果。目前已有356人学习下载。整体目录结构清晰,部署简单,下载后即可运行,能帮助读者快速掌握深度学习流量预测的完整实现思路与调参排错方法。
1. 纽约出租车流量预测:一份能直接跑通的大作业资源拆解
纽约出租车流量预测这个题目,在人工智能和深度学习课程的大作业里出现频率极高。原因很直接:它同时踩中了时空序列建模、真实城市数据集、多模型对比这三个评分点,导师一看就知道你动了手。但真正动手做过的人清楚,从原始流量数据到能画出 metrics 曲线,中间隔着一堆琐碎的坑——数据怎么切窗口、batch 和 hidden size 怎么配、CNN 和 LSTM 谁先谁后。这份资源把代码、数据集、说明文档和训练曲线图打包在一起,目录里有main.py、data_loader.py、configuration.py,模型侧给了gru.py、lstm.py、cnn_gru.py、cnn_lstm.py四套实现,还附了volume_train.npz和volume_test.npz两个已经处理好的流量文件。适合正在赶人工智能大作业、课程设计,或者想拿一个完整时空预测 pipeline 练手的人。下面按「资源是什么 → 怎么跑 → 坑在哪 → 怎么改出高分」的顺序拆开讲。
2. 资源结构与数据格式:先看清 npz 里装了什么
2.1 目录里每个文件对应哪一步
拿到压缩包先别急着python main.py,花两分钟把文件职责理清楚,后面调参和排错会省很多时间。这份资源的组织方式比较典型,属于「配置、数据、模型、入口」四层分离的结构。
| 文件 / 目录 | 作用 | 是否要改 |
|---|---|---|
configuration.py | 集中管理超参数:学习率、batch、hidden size、dropout、训练轮数 | 调参主战场 |
data_loader.py | 读取 npz、切滑动窗口、构造 DataLoader | 换数据集时改 |
func.py | 评估指标、归一化反变换等工具函数 | 一般不动 |
draw.py | 画 loss / metrics 曲线 | 改图样式时动 |
main.py | 训练入口,串起数据、模型、训练循环 | 换模型时动 |
gru.py/lstm.py | 单支循环网络模型 | 对比实验用 |
cnn_gru.py/cnn_lstm.py | CNN 提空间特征 + 循环网络提时序 | 主力模型 |
volume_train.npz/volume_test.npz | 已处理好的训练 / 测试流量张量 | 只读 |
NYC-stdn | 纽约出租车流量原始或中间数据目录 | 只读 |
images/ | 三张训练曲线图,文件名带超参 | 参考 |
数据说明.docx | 数据来源、字段、预处理说明 | 必读 |
images里那三张图的名字信息量很大:cnngru_lr0.001_b64_h64_d0.5_metrics.png、lstm_lr0.001_b64_h64_d0.5_metrics.png、cnnlstm_lr0.001_b64_h64_d0.5_metrics.png。它其实把作者跑通的一组可用超参直接写在了文件名里——学习率 0.001、batch 64、hidden 64、dropout 0.5。新手如果不想一上来就玄学调参,直接照这组数复现,是最稳的起点。
2.2 npz 数据的形状与滑动窗口
.npz是 NumPy 的压缩存档格式,本质是多个数组打包在一起。出租车流量预测里,常见的数据形状是(样本数, 时间步, 区域数, 通道数)或者(样本数, 时间步, 区域数)。加载和查看形状的代码大概是这样:
import numpy as np # 加载训练和测试流量数据 train_data = np.load('volume_train.npz') test_data = np.load('volume_test.npz') # 打印存档里所有数组的键名 print('train keys:', train_data.files) print('test keys:', test_data.files) # 逐个查看形状和取值范围,确认维度和量纲 for key in train_data.files: arr = train_data[key] print(key, arr.shape, arr.dtype, arr.min(), arr.max())这段代码的逻辑是先看键名,再逐个看形状、类型和数值范围。参数上要重点关注三件事:一是shape里的时间步维度在第几轴,决定了后面滑动窗口怎么切;二是dtype是float32还是float64,如果是整数型说明还没归一化;三是min/max,如果最大值是几百上千,那训练前必须做归一化,否则 loss 会炸。我一般会先把这些打印出来,再去看data_loader.py里窗口是怎么切的,两边对得上才继续。
2.3 滑动窗口是怎么把序列变成监督样本的
流量预测不是拿一整条序列直接喂进去,而是用「前 N 个时间步预测后 M 个时间步」的方式构造样本。data_loader.py里通常有一个类似split_windows或create_dataset的函数。理解这一步,你才知道configuration.py里那个window或seq_len参数到底在控制什么。
def create_windows(data, input_len, output_len): """把长序列切成 (输入窗口, 预测窗口) 的样本对""" x, y = [], [] total = len(data) # 从 0 开始滑动,保证最后一个窗口不越界 for i in range(total - input_len - output_len + 1): x.append(data[i:i + input_len]) # 历史输入 y.append(data[i + input_len:i + input_len + output_len]) # 待预测 return np.array(x), np.array(y)逻辑说明:input_len是回看的历史步数,output_len是要预测的未来步数,循环上界减掉两者再加一是为了防止索引越界。参数上,input_len太小模型看不到周期规律,太大则样本数骤减、训练变慢;出租车流量有很强的日周期和周周期,常见做法是把input_len设成一天或一周对应的步数。这里最容易翻车的是窗口切完后样本数对不上,训练时 batch 凑不齐最后一个,所以DataLoader里一般要开drop_last=True,或者手动补齐。
3. 环境搭建与训练跑通:从零到第一条 metrics 曲线
3.1 依赖安装与版本对齐
这份代码的缓存目录是__pycache__/configuration.cpython-39.pyc,说明作者用的是 Python 3.9。深度学习项目对版本敏感,尤其是 PyTorch 和 NumPy,版本错位经常报一些看不懂的错。稳妥起见,先建虚拟环境再装依赖。
# 创建并激活虚拟环境,避免污染全局 python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate # 安装核心依赖,版本按本机 CUDA 情况调整 pip install numpy pandas matplotlib pip install torch torchvision # 有 GPU 时去官网选对应 CUDA 版本逻辑说明:先隔离环境,再装依赖。torch的安装命令不要照抄,要去官方安装页按自己的 CUDA 版本选,否则装成 CPU 版训练会慢到怀疑人生。参数上,如果机器没有独显,就老老实实装 CPU 版,把configuration.py里的 batch 调小,不然内存扛不住。装完先python -c "import torch; print(torch.__version__, torch.cuda.is_available())"确认一下,这一步能省掉后面一半的报错排查。
3.2 配置文件的参数逐个说清
configuration.py是整个项目的控制面板,训练前必须逐项确认。下面这张表把常见参数和调整方向列出来,照着改不会迷路。
| 参数 | 含义 | 常见取值 | 调整建议 |
|---|---|---|---|
lr | 学习率 | 0.001 | 太大 loss 震荡,太小收敛慢 |
batch_size | 批大小 | 64 | 显存不够就降到 32 / 16 |
hidden_size | 循环层隐藏维度 | 64 | 越大拟合越强,也越容易过拟合 |
dropout | 丢弃率 | 0.5 | 过拟合时加大,欠拟合时减小 |
epochs | 训练轮数 | 50~100 | 配合早停,别硬跑 |
input_len | 回看步数 | 按数据周期定 | 与数据采样间隔匹配 |
output_len | 预测步数 | 1 或多步 | 多步预测难度陡增 |
参数之间是联动的。比如你把hidden_size从 64 提到 256,显存占用会明显上升,这时batch_size就得往下压;dropout提到 0.5 以上虽然能压过拟合,但训练 loss 会下降得很慢,别误以为代码坏了。作者在文件名里留下的那组lr0.001_b64_h64_d0.5是经过验证的平衡点,建议先原样跑通,再逐个改。
3.3 启动训练并读懂输出
配置确认后,入口就是main.py。运行前先确认当前工作目录在项目根目录,否则相对路径读 npz 会直接报FileNotFoundError。
# 在项目根目录运行训练入口 python main.py运行后终端会打印每个 epoch 的 loss 和评估指标,log.txt里也会留一份记录。判断训练是否正常,看三点:第一,训练 loss 是否整体下降,如果一直不降,多半是学习率太大或数据没归一化;第二,验证 loss 是否在某个点后开始回升,那是过拟合的信号,该加 dropout 或早停;第三,draw.py画出来的曲线是否和images/里那三张图趋势一致。如果一致,说明你复现成功了,可以进入改模型阶段。
3.4 四套模型怎么选、怎么切
资源给了gru.py、lstm.py、cnn_gru.py、cnn_lstm.py四个模型,这不是凑数,而是让你做对比实验的。单支的 GRU / LSTM 只建模时间维,参数量小、跑得快,适合先验证数据 pipeline 通不通;CNN-GRU / CNN-LSTM 先用卷积提空间(区域间)特征,再用循环网络提时间特征,效果通常更好,但训练更慢、调参更敏感。
切换模型一般是在main.py里改导入和实例化那一行:
# 从不同模型文件导入,切换对比实验 from cnn_lstm import CNNLSTM # 换成 from cnn_gru import CNNGRU 即可对比 from configuration import config model = CNNLSTM(config) # 实例化,传入配置 model = model.to(config.device) # 放到 GPU 或 CPU逻辑说明:导入哪个类,就实例化哪个模型,其余训练循环不用动。参数上要注意,不同模型对hidden_size的敏感度不一样,CNN 部分还有自己的卷积核和通道数,如果configuration.py里没暴露这些,就得进模型文件里改。做对比实验时,除了模型结构,其他超参必须保持一致,否则结论不成立——这是大作业拿高分的关键,导师最看重变量控制。
4. 避坑与排查:训练跑不通时先看这几条
4.1 报错 FileNotFoundError 找不到 npz
现象:一运行main.py就抛FileNotFoundError: volume_train.npz。原因:代码里用的是相对路径,而你在别的目录下执行,或者解压后多套了一层文件夹。解决:先cd到含 npz 的目录再运行,或者把data_loader.py里的路径改成基于__file__的绝对路径,一劳永逸。
4.2 loss 变成 nan 或一直不下降
现象:训练几个 batch 后 loss 显示nan,或者几十轮都不降。原因:数据没归一化,原始流量值太大;或者学习率设得过高。解决:在data_loader.py里加最大最小归一化,把数据压到 0~1;同时把lr从 0.001 往下调到 0.0001 试。归一化后记得在评估时反变换回去,否则指标没有物理意义。
4.3 显存不足 CUDA out of memory
现象:训练中途报显存溢出。原因:batch_size或hidden_size太大,或者没有及时释放中间变量。解决:先把batch_size减半,再把hidden_size降到 32 试;如果还不行,在训练循环里对 loss 做detach(),并定期torch.cuda.empty_cache()。CPU 训练的话,把epochs和样本量都压小,先跑通再说。
4.4 训练 loss 降但验证指标很差
现象:训练集 loss 一路走低,验证集指标却原地踏步甚至变差。原因:过拟合,模型把训练样本背下来了。解决:把dropout从 0.5 提到 0.6~0.7,加 L2 正则(权重衰减),或者直接上早停——验证 loss 连续几轮不降就停。数据量小的时候,这一条几乎必踩。
4.5 换了模型但结果完全一样
现象:从 LSTM 换成 CNN-LSTM,指标一模一样。原因:main.py里模型实例化那行没改,或者改了导入但没重新实例化,跑的还是旧模型。解决:在实例化后打印model.__class__.__name__确认,同时清掉__pycache__再跑,避免加载到旧的.pyc缓存。
5. 进阶技巧:把大作业做出对比实验的深度
5.1 用统一脚本批量跑对比实验
单跑一个模型只能证明「能跑」,跑一组对比才能证明「你懂」。与其手动改一次配置跑一次,不如写个循环,把四套模型和几组超参批量跑完,结果统一存表。这样既省事,又能直接产出大作业里最值钱的那张对比表。
import subprocess import itertools # 待对比的模型入口和超参组合 models = ['lstm', 'gru', 'cnn_lstm', 'cnn_gru'] lrs = [0.001, 0.0005] results = [] for model_name, lr in itertools.product(models, lrs): # 通过环境变量或临时改配置传入超参,避免手改文件 cmd = f"python main.py --model {model_name} --lr {lr}" subprocess.run(cmd, shell=True, check=True) results.append((model_name, lr)) print(f"finished: {model_name} lr={lr}") print("all done:", results)逻辑说明:用itertools.product生成模型和超参的笛卡尔积,逐个调用训练入口。参数上,--model和--lr需要你在main.py里用argparse接一下,否则命令传不进去。跑完后把每个组合的验证指标汇总成表,横向对比 RMSE / MAE,谁强谁弱一目了然。这一步做完,你的大作业就从「跑通」升级到「有实验设计」。
5.2 指标怎么算才经得起追问
流量预测常用 RMSE、MAE、MAPE 三个指标。RMSE 对大误差敏感,MAE 更稳健,MAPE 是百分比误差但遇到零值会炸。计算前一定要把预测值反归一化回原始量纲,否则算出来的数没有意义。下面是一个最小实现:
import numpy as np def evaluate(y_true, y_pred): """输入已反归一化的真实值和预测值""" y_true, y_pred = np.array(y_true), np.array(y_pred) rmse = np.sqrt(np.mean((y_true - y_pred) ** 2)) mae = np.mean(np.abs(y_true - y_pred)) # 加极小值防止除零 mape = np.mean(np.abs((y_true - y_pred) / (y_true + 1e-8))) * 100 return {'RMSE': rmse, 'MAE': mae, 'MAPE': mape}逻辑说明:三个指标从不同角度衡量误差,答辩时导师很可能追问「为什么用 RMSE 不用 MAE」,你得答得上来。参数上,1e-8是防止真实值为零时除零,这是血泪经验——出租车流量在某些时段某些区域确实可能是零。另外,多步预测时建议分步报告指标,而不是只报一个平均值,这样能看出模型在预测第几步开始崩。
5.3 从「能跑」到「有说服力」的最后一公里
跑通代码只是及格线,想拿高分还得补三样东西:一是数据可视化,把真实流量和预测流量画在同一张图上,直观展示拟合程度;二是误差分析,找出模型在哪些时段预测最差,通常早晚高峰和异常天气是重灾区;三是消融实验,证明 CNN 分支和循环分支各自都有贡献,去掉哪个指标都会掉。这三样做完,你的项目就不再是「下载下来跑了一遍」,而是「我理解并改进了它」。
我自己的习惯是,每次拿到这类资源,先原样跑通拿到 baseline,再动任何一个参数前都记一笔,最后把对比结果整理成表。从那以后我每次做大作业都强制走一遍「baseline → 单变量对比 → 消融」的流程,再也没被问倒过。希望这份拆解能帮你少走几个弯路,顺利把项目跑起来、讲清楚。
本文还有配套的精品资源,点击获取