METR-LA数据预处理实战从原始H5到时空预测模型的完美输入第一次打开METR-LA的.h5文件时那种面对34272×207的二维数据矩阵的茫然感至今记忆犹新。交通预测领域的论文总是优雅地展示着模型架构图却很少提及数据预处理这个脏活累活。本文将带你一步步拆解这个看似复杂的数据转换过程用NumPy和Pandas将原始数据转化为模型真正需要的N×T×12格式。1. 理解METR-LA数据的基本结构METR-LA数据集记录了洛杉矶207个传感器在4个月2012年3月-6月内每5分钟一次的交通速度数据。原始数据存储为HDF5格式用Pandas读取后得到的是一个34272行×207列的DataFrameimport pandas as pd df pd.read_hdf(metr-la.h5) print(df.shape) # (34272, 207)关键特性解析行索引时间戳从2012-03-01 00:00:00开始每5分钟一个间隔列索引传感器ID从0到206数值交通速度mph常见新手错误包括直接尝试reshape数据导致维度错乱忽略时间戳的连续性检查错误理解传感器ID的物理意义提示使用df.head()和df.describe()快速检查数据分布确保没有异常值或缺失值2. 构建时间滑动窗口的核心逻辑时空预测模型如DCRNN、STGCN通常采用滑动窗口机制用过去12个时间步预测未来12个时间步。这需要精心设计offset策略import numpy as np # 输入窗口过去12个时间点包括当前时刻 x_offsets np.arange(-11, 1, 1) # [-11, -10,..., 0] # 输出窗口未来12个时间点不包括当前时刻 y_offsets np.arange(1, 13, 1) # [1, 2,..., 12]为什么这样设计offset输入窗口包含当前时刻t0及其前11个时间点输出窗口从t1开始避免信息泄露这种设计确保模型只能基于历史数据预测未来3. 时间特征工程实战技巧许多模型会利用时间周期性特征如一天中的相对时间。以下是提取时间特征的实用方法# 计算当前时刻在一天中的相对位置0-1之间 time_ind (df.index.values - df.index.values.astype(datetime64[D])) / np.timedelta64(1, D) # 扩展为[34272, 207, 1]的张量 time_in_day np.tile(time_ind, [1, num_nodes, 1]).transpose((2, 1, 0))维度变换详解time_ind初始形状(34272,)经过np.tile扩展(1, 207, 34272)转置后得到(34272, 207, 1)注意这一步不是所有模型都必需但保持与主流实现一致有助于结果对比4. 数据整合与滑动窗口生成将交通数据和时间特征合并后就可以生成最终的输入输出对# 原始交通数据增加维度 [34272, 207, 1] data np.expand_dims(df.values, axis-1) # 合并交通数据和时间特征 [34272, 207, 2] data np.concatenate([data, time_in_day], axis-1) # 滑动窗口生成 x, y [], [] min_t abs(min(x_offsets)) # 11 max_t abs(data.shape[0] - abs(max(y_offsets))) # 34260 for t in range(min_t, max_t): x.append(data[t x_offsets]) y.append(data[t y_offsets]) x np.stack(x) # [34249, 12, 207, 2] y np.stack(y) # [34249, 12, 207, 2]窗口生成过程可视化窗口类型时间点序列示例实际索引范围输入窗口t-11到t[0:12] → [11:23]输出窗口t1到t12[12:24] → [23:35]5. 数据集划分与保存最佳实践按照7:1:2的比例划分训练集、验证集和测试集num_samples x.shape[0] num_test round(num_samples * 0.2) num_train round(num_samples * 0.7) num_val num_samples - num_test - num_train # 切片获取各数据集 x_train, y_train x[:num_train], y[:num_train] x_val, y_val x[num_train:num_trainnum_val], y[num_train:num_trainnum_val] x_test, y_test x[-num_test:], y[-num_test:] # 保存为压缩格式 np.savez_compressed( train.npz, xx_train, yy_train, x_offsetsx_offsets, y_offsetsy_offsets )数据集划分要点保持时间连续性不要随机打乱验证集用于早停early stopping测试集只在最终评估时使用6. 常见问题排查指南问题1维度不匹配错误检查data张量的形状是否为[时间步, 节点数, 特征数]确保x_offsets和y_offsets长度相同问题2索引越界错误确认min_t和max_t计算正确检查原始数据是否有缺失时间点问题3模型训练时loss不下降验证输入输出对的对应关系是否正确检查数据标准化是否合理7. 高级技巧内存优化策略处理大规模时空数据时内存管理至关重要# 使用生成器替代列表存储 def data_generator(data, x_offsets, y_offsets, batch_size32): min_t abs(min(x_offsets)) max_t abs(data.shape[0] - abs(max(y_offsets))) while True: batch_x, batch_y [], [] for t in np.random.choice(range(min_t, max_t), batch_size): batch_x.append(data[t x_offsets]) batch_y.append(data[t y_offsets]) yield np.stack(batch_x), np.stack(batch_y)优化方案对比方法内存占用适用场景全量加载高小数据集快速实验生成器低大数据集或有限内存内存映射中频繁访问的中间数据在实际项目中我通常会先在小样本上验证流程正确性再应用这些优化策略处理完整数据集。记得在处理完成后及时使用del释放大对象特别是交互式开发时。