该实验使用了ConvLSTM模型,对伦敦地区的空气质量进行了时序预测。数据集来源于开源库openair。实验的目标是预测Bloomsbury的空气污染物数值。同时,也利用了Harlington, North Kensington, Marylebone 和Eltham这四个空气质量监测站的数据作为辅助预测。数据的属性有8个,分别为:NOX, NO2, NO, O3, PM2.5, 风速,风向和空气温度。
除了使用ConvLSTM, 该实验还使用了普通LSTM, BiLSTM, Attention+LSTM, LightGBM 和ARIMA进行预测。具体内容可以在github上进行查看:air_pollutants_prediction_lstm。
假定Marylebone Road监测站的数据出现异常,此时我们用Bloomsbury, Eltham, Harlington和N_Kensington的数据来推断Marylebone的数据。该实验是多变量预测单变量。分多次预测,实现多变量预测多变量。
#读取数据,数据已经清洗过了。 Marylebone_Road=pd.read_csv('/content/drive/My Drive/air_inference/data/Marylebone_Road_clean.csv') Bloomsbury=pd.read_csv('/content/drive/My Drive/air_inference/data/Bloomsbury_clean.csv') Eltham=pd.read_csv('/content/drive/My Drive/air_inference/data/Eltham_clean.csv') Harlington=pd.read_csv('/content/drive/My Drive/air_inference/data/Harlington_clean.csv') N_Kensington=pd.read_csv('/content/drive/My Drive/air_inference/data/N_Kensington_clean.csv')只选取我们想要的数据属性,NOX, NO2, NO, O3, PM2.5, 风速,风向和空气温度。
Marylebone_Road=Marylebone_Road[['nox','no2','no','o3','pm2.5','ws','wd','air_temp']] Bloomsbury=Bloomsbury[['nox','no2','no','o3','pm2.5','ws','wd','air_temp']] Eltham=Eltham[['nox','no2','no','o3','pm2.5','ws','wd','air_temp']] Harlington=Harlington[['nox','no2','no','o3','pm2.5','ws','wd','air_temp']] N_Kensington=N_Kensington[['nox','no2','no','o3','pm2.5','ws','wd','air_temp']]先将每个监测站的columns更名。加上后缀为监测站的首字母
col_Marylebone=['nox_M','no2_M','no_M','o3_M','pm2.5_M','ws_M','wd_M','air_temp_M'] col_Bloomsbury=['nox_B','no2_B','no_B','o3_B','pm2.5_B','ws_B','wd_B','air_temp_B'] col_Eltham=['nox_E','no2_E','no_E','o3_E','pm2.5_E','ws_E','wd_E','air_temp_E'] col_Harlington=['nox_H','no2_H','no_H','o3_H','pm2.5_H','ws_H','wd_H','air_temp_H'] col_N_Kensington=['nox_N','no2_N','no_N','o3_N','pm2.5_N','ws_N','wd_N','air_temp_N'] Marylebone_Road.columns=col_Marylebone Bloomsbury.columns=col_Bloomsbury Eltham.columns=col_Eltham Harlington.columns=col_Harlington N_Kensington.columns=col_N_Kensington接下来将所有监测站的数据拼接起来
dataset=Bloomsbury.join(Marylebone_Road) dataset=dataset.join(Eltham) dataset=dataset.join(Harlington) dataset=dataset.join(N_Kensington)此步骤包括
进行归一化操作数据转为cuda类型 (我们要用GPU进行加速)划分训练集,验证集和测试集获得训练集,验证集以及测试集的时间窗口归一化操作。注意范围去(0,1),如果取(-1,1),结果惨不忍睹。
var_origin=dataset.values from sklearn.preprocessing import MinMaxScaler scaler = MinMaxScaler(feature_range=(0, 1)) scaled = scaler.fit_transform(var_origin)转为CUDA
import torch device = torch.device("cuda" if torch.cuda.is_available() else "cpu") var= torch.FloatTensor(scaled).to(device)划分训练集,验证集和测试集
def splitData(var,per_val,per_test): num_val=int(len(var)*per_val) num_test=int(len(var)*per_test) train_size=int(len(var)-num_val-num_test) train_data=var[0:train_size] val_data=var[train_size:train_size+num_val] test_data=var[train_size+num_val:train_size+num_val+num_test] return train_data,val_data,test_data剩下的是创建训练集,验证集和测试集的时间窗口。这里只放创建训练集窗口的函数。
ConLSTM的代码借鉴的是Github一位老哥写的。ConvLSTM。
import torch.nn as nn import torch class ConvLSTMCell(nn.Module): def __init__(self, input_dim, hidden_dim, kernel_size, bias): """ Initialize ConvLSTM cell. Parameters ---------- input_dim: int Number of channels of input tensor. hidden_dim: int Number of channels of hidden state. kernel_size: (int, int) Size of the convolutional kernel. bias: bool Whether or not to add the bias. """ super(ConvLSTMCell, self).__init__() self.input_dim = input_dim self.hidden_dim = hidden_dim self.kernel_size = kernel_size self.padding = kernel_size[0] // 2, kernel_size[1] // 2 self.bias = bias self.conv = nn.Conv2d(in_channels=self.input_dim + self.hidden_dim, out_channels=4 * self.hidden_dim, kernel_size=self.kernel_size, padding=self.padding, bias=self.bias).to(device) def forward(self, input_tensor, cur_state): h_cur, c_cur = cur_state combined = torch.cat([input_tensor, h_cur], dim=1) # concatenate along channel axis combined_conv = self.conv(combined) cc_i, cc_f, cc_o, cc_g = torch.split(combined_conv, self.hidden_dim, dim=1) i = torch.sigmoid(cc_i) f = torch.sigmoid(cc_f) o = torch.sigmoid(cc_o) g = torch.tanh(cc_g) c_next = f * c_cur + i * g h_next = o * torch.tanh(c_next) return h_next.to(device), c_next.to(device) def init_hidden(self, batch_size, image_size): height, width = image_size return (torch.zeros(batch_size, self.hidden_dim, height, width, device=self.conv.weight.device), torch.zeros(batch_size, self.hidden_dim, height, width, device=self.conv.weight.device))这个只是cell的类,还有一个ConvLSTM的类因为太长,就不写啦,需要的看详细的,请点击 air_pollutants_prediction_lstm。
这里只用一个预测NOX的模型的结果来讲一下评估模型的好坏。
我们测试集是有1752个窗口,每个窗口,模型将会预测出一个值,即下一个小时的nox的值,那么只需要对这1752个窗口做MAE和MSE就可以知道模型在预测下一小时时候的能力。注意,模型每次预测的值将不会被塞进下一个时间窗口中,这与接下来要讲的96小时预测能力评估不同。
96小时预测能力。即,一共有96个时间窗口,每一个时间窗口预测出来的值,是会被被重新插入到下一个时间窗口的最后一位,从而挤出了原来时间窗口的第一位。这样的评估出来的模型,具备预测未来96小时的能力。因为预测值会被带入下一个窗口,经过几次预测,预测的误差会被积累放大,所以只预测96小时。
值得注意的是,当我通过预测1小时能力,选出具有潜力的模型,然后才会对它做96小时预测能力的评估。
正如我开头提到的,我不仅仅只用了ConLSTM,我也用了其他算法。可以到我的github去看看哈。也希望能得到宝贵的意见。