今天博主来分享一个车流量预测的深度学习基础实战项目,本次项目采用的数据集为纽约市各区域的历史车流量,输出为对应各区域未来的车流量,话不多说,请看下文!!!
本实验的数据集为纽约出租车轨迹数据集,数据集中记录了纽约市200个区域(划分得到)从2015年1月1号到2015年3月1号的车流量,记录的时间间隔为30分钟。为方便理解数据集,先放一张类似于该数据集的数据形式的沿时间顺序排列的时间栅格数据图,可知每个时间点纽约市各区域的车流量就是某个时间点的时间栅格。
对于该数据集,训练集与测试集已经事先划分好,其中后20天作为测试集(volume_test.npz
),而剩余的则作为训练集(volume_train.npz
),其中训练集包括1920个时间点的车流量数据,而测试集包括960个时间点的车流量数据。注意,每个小区域都包含入流量和出流量两个特征。
对于时序数据,一种常规的处理流程按时间顺序来进行滑窗截取短序列,本次实验中使用的滑窗size等于7,即用6个历史时间步的历史车流量来预测最后一个时间步的车流量。另外,在实验的过程中为了使得求解过程更加容易,还使用了归一化操作,即:
X i ′ = X i − X m i n X m a x − X m i n X_{i}' = \frac{X_i - X_{min}}{X_{max} - X_{min}} Xi′=Xmax−XminXi−Xmin
将上述操作封装成类展示为如下源码:
import torch
import numpy as np
class NYCSTDNDataset():
def __init__(self,data_path,window_size=7) -> None:
self.window_size = window_size
self.data = torch.from_numpy(self.loading(data_path)).float()
def loading(self,data_path):
data = np.load(data_path)['volume']
# print(data.shape)
self.max_val,self.min_val = np.max(data),np.min(data)
dataset = slidingWindow(data,self.window_size)
dataset = np.array(dataset).transpose(0,1,4,2,3) # (1914, 7, 2, 10, 20)
dataset = dataset.reshape(dataset.shape[0],dataset.shape[1],-1)
dataset = (dataset - self.min_val) / (self.max_val - self.min_val)
return dataset
def denormalize(self,x):
return x * (self.max_val - self.min_val) + self.min_val
def slidingWindow(seqs,size):
"""
seqs: ndarray sequence, shape(seqlen,area_nums,2)
size: sliding window size
"""
result = []
for i in range(seqs.shape[0] - size + 1):
result.append(seqs[i:i + size,:,:,:]) #(7, 10, 20, 2)
# print(np.array(result).shape)
return result
如此一来,我们即可以通过传入原始数据的路径来获取到与预处理好的数据:
train_path = 'NYC-stdn/volume_train.npz'
dataset = NYCSTDNDataset(train_path)
print(dataset.data.shape)
test_path = 'NYC-stdn/volume_test.npz'
dataset = NYCSTDNDataset(test_path)
print(dataset.data.shape)
"""
torch.Size([1914, 7, 400])
torch.Size([954, 7, 400])
"""
对于序列预测数据,很容易想到的便是利用RNN,为此本文也可以直接使用类似我之前一篇文章《疫情微博内容情感分析》中设计的模型来进行训练与预测。但对于时间栅格数据,某个地点的交通量不仅与其邻近节点有关、还有与其邻近时间点的交通量有关,这有点类似于三维立体结构。为此本实验借鉴特征融合的思想,在LSTM的基础上添加了3D卷积来提取额外的特征并与LSTM提取到的特征进行融合,最后进行预测,下图是该设计方案的模型结构图。
本次实验数据的维度(样本数、滑动窗口大小、特征维度、栅格高度、栅格宽度)
与三维卷积的参数(N, D_in, C_in, H_in, W_in)
一一对应。下面给出该模型的源码:
import torch
import torch.nn as nn
import torch.nn.functional as F
class CNNLSTM(nn.Module):
def __init__(self,in_channel,out_channels,input_size,hidden_size,output_size,drop_prob):
super(CNNLSTM,self).__init__()
self.convs = nn.Sequential(
nn.Conv3d(in_channels=in_channel, out_channels=out_channels[0], kernel_size=(3, 3, 3)),
nn.MaxPool3d(kernel_size=(1, 2, 2), stride=(1, 2, 2)),
nn.Conv3d(in_channels=out_channels[0], out_channels=out_channels[1], kernel_size=(3, 3, 3)),
nn.MaxPool3d(kernel_size=(2, 2, 2), stride=(2, 2, 2))
)
self.lstm = nn.LSTM(input_size=input_size,
hidden_size=hidden_size,
batch_first=True,
num_layers=2)
self.dropout = nn.Dropout(drop_prob)
self.fc = nn.Linear(384 + hidden_size,output_size)
def forward(self,x):
"""
x: (batch,depth/seqlen,channels*h*w)
"""
x = x.view(x.shape[0],x.shape[1],2,10,20)
cnn_feats = self.convs(x.transpose(1,2))
# print(cnn_feats.shape)
gru_out,_ = self.lstm(x.view(x.shape[0],x.shape[1],-1))
gru_feats =torch.mean(gru_out,dim=1)
fusion_feats = torch.cat((cnn_feats.view(cnn_feats.shape[0],-1),gru_feats),dim=1)
x = self.fc(self.dropout(fusion_feats))
return F.sigmoid(x)
本实验中模型CNNLSTM的输入维度为2,两个卷积层的卷积核个数分别为64和128,模型的输出为400( 10 × 20 10 \times 20 10×20个区域的出入流量的预测值)
model = CNNLSTM(
in_channel=2,
out_channels=[64,128],
input_size=400,
hidden_size=64,
output_size=400,
drop_prob=0.5)
本次实验过程中使用的评估指标包括RMSE
、MAE
和MAPE
,实验中的超级参数如下表所示:
参数 | 值 |
---|---|
lr | 0.001 |
epochs | 1000 |
batch size | 64 |
drop prob | 0.5 |
利用该模型进行训练的截图如下所示:
另外,本次实验还绘制了RMSE
、MAE
和MAPE
和loss在训练过程中的变化情况:
完整项目的源码(有条件的请支持一下,感谢!!!)
以上便是本文的全部内容,要是觉得不错的话就点个赞或关注一下博主吧,你们的支持是博主继续创作的不解动力,当然若是有任何问题也敬请批评指正!!!