当数据有多个季节周期,很可能会导致残差不是正态分布并且相关,因此无法使用SARIMAX 模型,此时便可以通过深度学习利用两个季节的信息来做出预测。
对于时间序列预测,一般可以创建三种类型深度学习模型: 单步模型、多步模型和多输出模型。单步模式是三种模式中最简单的一种,其输入可以是任意长度,输出是一个单一的值,代表着对未来一个变量的预测。
线性模型是深度学习中可以实现的最简单的架构,其没有隐藏层,每个输入特征都被简单地赋予一个权重,然后通过组合输出对目标的预测,如同传统的线性回归。
激活函数在神经网络的每个神经元中,负责从输入数据中生成输出。其可分为线性激活函数与非线性激活函数,如果使用线性激活函数,模型将只建模线性关系;而为了模拟数据中的非线性关系,则须使用非线性激活函数,非线性激活函数大致分为:ReLU、softmax、tanh。其中ReLU是深度学习中使用最广泛的激活函数,一般在一个密集层和一个隐藏层时使用。
通过数据概览以及图像观测,可发现traffic_volume数据以星期为周期反复变化,并且rain_1h与snow_1h基本为0,对于预测帮助不大,可移除,另外时间属性字段需要特殊处理。
df = pd.read_csv('metro_interstate_traffic_volume_preprocessed.csv')
print(df.head())
print(df.shape)
print(df.describe().transpose())
date_time temp rain_1h snow_1h clouds_all traffic_volume
0 2016-09-29 17:00:00 291.75 0.0 0 0 5551.0
1 2016-09-29 18:00:00 290.36 0.0 0 0 4132.0
2 2016-09-29 19:00:00 287.86 0.0 0 0 3435.0
3 2016-09-29 20:00:00 285.91 0.0 0 0 2765.0
4 2016-09-29 21:00:00 284.31 0.0 0 0 2443.0
(17551, 6)
count mean std ... 50% 75% max
temp 17551.0 281.416203 12.688262 ... 282.41 291.89 310.07
rain_1h 17551.0 0.025523 0.259794 ... 0.00 0.00 10.60
snow_1h 17551.0 0.000000 0.000000 ... 0.00 0.00 0.00
clouds_all 17551.0 42.034129 39.065960 ... 40.00 90.00 100.00
traffic_volume 17551.0 3321.484588 1969.223949 ... 3518.00 4943.00 7280.00[5 rows x 8 columns]
a、删除rain_1h与snow_1h字段
cols_to_drop = ['rain_1h', 'snow_1h']
df = df.drop(cols_to_drop, axis=1)
b、时间属性字段转换
将时间属性数据通过正弦、余弦方法转换,保留其循环周期性
timestamp_s = pd.to_datetime(df['date_time']).map(datetime.datetime.timestamp)
day = 24 * 60 * 60
df['day_sin'] = (np.sin(timestamp_s * (2*np.pi/day))).values
df['day_cos'] = (np.cos(timestamp_s * (2*np.pi/day))).values
df = df.drop(['date_time'], axis=1)
1.3 数据拆分
将数据集按7:2:1拆分为训练集、验证集与测试集
n = len(df)
train_df = df[0:int(n*0.7)]
val_df = df[int(n*0.7):int(n*0.9)]
test_df = df[int(n*0.9):]
1.4 数据缩放与保存
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
scaler.fit(train_df)
train_df[train_df.columns] = scaler.transform(train_df[train_df.columns])
val_df[val_df.columns] = scaler.transform(val_df[val_df.columns])
test_df[test_df.columns] = scaler.transform(test_df[test_df.columns])
train_df.to_csv('data/train.csv')
val_df.to_csv('data/val.csv')
test_df.to_csv('data/test.csv')
count mean std ... 50% 75% max
temp 12285.0 0.555681 0.196527 ... 0.557913 0.715072 1.0
clouds_all 12285.0 0.431034 0.393716 ... 0.400000 0.900000 1.0
traffic_volume 12285.0 0.445735 0.273635 ... 0.473141 0.671271 1.0
day_sin 12285.0 0.499885 0.353534 ... 0.500000 0.853553 1.0
day_cos 12285.0 0.500031 0.353602 ... 0.500000 0.853553 1.0
主要控制输入长度、标签长度、移位以及所需预测的字段,通过这些参数决定预测的范围。
class DataWindow():
def __init__(self, input_width, label_width, shift,
train_df=train_df, val_df=val_df, test_df=test_df,
label_columns=None):
self.train_df = train_df
self.val_df = val_df
self.test_df = test_df
self.label_columns = label_columns # 需要预测的列名
if label_columns is not None:
self.label_columns_indices = {name: i for i, name in enumerate(label_columns)} # 名称和索引为标签列,用于绘图
self.column_indices = {name: i for i, name in enumerate(train_df.columns)} # 用于从目标变量分离特性
self.input_width = input_width
self.label_width = label_width
self.shift = shift
self.total_window_size = input_width + shift
self.input_slice = slice(0, input_width) # 对序列进行切片
self.input_indices = np.arange(self.total_window_size)[self.input_slice] #分配索引,用于绘图
self.label_start = self.total_window_size - self.label_width #获取索引标签起始位置,总窗口大小减去标签宽度
self.labels_slice = slice(self.label_start, None) #对标签序列进行切片
self.label_indices = np.arange(self.total_window_size)[self.labels_slice]
def split_to_inputs_labels(self, features):
inputs = features[:, self.input_slice, :] #切片窗口用于输入的使用
labels = features[:, self.labels_slice, :] #切片窗口用于标签的使用
if self.label_columns is not None: #如果有多个目标,则堆叠标签
labels = tf.stack(
[labels[:, :, self.column_indices[name]] for name in self.label_columns],
axis=-1
)
inputs.set_shape([None, self.input_width, None]) #此处只指定时间维度[batch,time, features]
labels.set_shape([None, self.label_width, None])
return inputs, labels
def plot(self, model=None, plot_col='traffic_volume', max_subplots=3):
inputs, labels = self.sample_batch
plt.figure(figsize=(12, 8))
plot_col_index = self.column_indices[plot_col]
max_n = min(max_subplots, len(inputs))
for n in range(max_n):
plt.subplot(3, 1, n + 1)
plt.ylabel(f'{plot_col} [scaled]')
plt.plot(self.input_indices, inputs[n, :, plot_col_index],
label='Inputs', marker='.', zorder=-10) #绘制输入,连续的蓝线
if self.label_columns:
label_col_index = self.label_columns_indices.get(plot_col, None)
else:
label_col_index = plot_col_index
if label_col_index is None:
continue
plt.scatter(self.label_indices, labels[n, :, label_col_index],
edgecolors='k', marker='s', label='Labels', c='green', s=64) #绘制标签或实际数值,绿色方块
if model is not None:
predictions = model(inputs)
plt.scatter(self.label_indices, predictions[n, :, label_col_index],
marker='X', edgecolors='k', label='Predictions',
c='red', s=64) #绘制预测,红十字
if n == 0:
plt.legend()
plt.xlabel('Time (h)')
def make_dataset(self, data):
data = np.array(data, dtype=np.float32)
ds = tf.keras.preprocessing.timeseries_dataset_from_array(
data=data, #对应于训练集、验证集或测试集
targets=None, #设置为none,因为由函数split_to_inputs_labels处理
sequence_length=self.total_window_size, #定义数组总长度,等同于总窗口长度
sequence_stride=1, #定义每个序列的时间步长,此处希望序列是连续的,因此设置为1
shuffle=True, #排序序列,数据仍然按时间顺序排列
batch_size=32 #定义单个批处理的序列数
)
ds = ds.map(self.split_to_inputs_labels)
return ds
@property
def train(self):
return self.make_dataset(self.train_df)
@property
def val(self):
return self.make_dataset(self.val_df)
@property
def test(self):
return self.make_dataset(self.test_df)
@property
def sample_batch(self): #获取用于绘图的示例数据,如果样本批处理不存在,将检索一个样本批处理并换成它
result = getattr(self, '_sample_batch', None)
if result is None:
result = next(iter(self.train))
self._sample_batch = result
return result
将最后的观测值作为预测结果
class Baseline(Model):
def __init__(self, label_index=None):
super().__init__()
self.label_index = label_index
def call(self, inputs):
if self.label_index is None: #如果未指定目标,则返回所有列,用于预测所有列的多输出模型
return inputs
else:
result = inputs[:, :, self.label_index] #返回给定目标变量的输入
return result[:, :, tf.newaxis]
single_step_window = DataWindow(input_width=1, label_width=1, shift=1, label_columns=['traffic_volume'])
wide_window = DataWindow(input_width=24, label_width=24, shift=1, label_columns=['traffic_volume'])
column_indices = {name: i for i, name in enumerate(train_df.columns)} #生成训练集中每一列的索引
baseline_last = Baseline(label_index=column_indices['traffic_volume']) #传输目标列索引的基线类
baseline_last.compile(loss=MeanSquaredError(), metrics=[MeanAbsoluteError()]) #编译模型生成预测
val_performance = {} #保存验证集的MAE的字典
performance = {} #保存测试集的MAE的字典
val_performance['Baseline - Last'] = baseline_last.evaluate(single_step_window.val) #存储验证集基准线的MAE
performance['Baseline - Last'] = baseline_last.evaluate(single_step_window.test, verbose=0) #存储测试集基准线的MAE
wide_window.plot(baseline_last)
plt.show()
linear = Sequential([
Dense(units=1) #把单位数量指定为 1,因为模型只能输出一个值: 下一个时间步长的交通流量预测
])
history = compile_and_fit(linear, single_step_window) #训练模型
val_performance['Linear'] = linear.evaluate(single_step_window.val)
performance['Linear'] = linear.evaluate(single_step_window.test, verbose=0)
wide_window.plot(linear)
plt.show()
dense = Sequential([
Dense(units=64, activation='relu'), #第一个隐藏层有 64 个神经元。指定激活函数为 ReLU
Dense(units=64, activation='relu'),
Dense(units=1) #输出层只有一个神经元,因为我们只输出一个值
])
history = compile_and_fit(dense, single_step_window)
val_performance['Dense'] = dense.evaluate(single_step_window.val)
performance['Dense'] = dense.evaluate(single_step_window.test, verbose=0)
wide_window.plot(dense)
plt.show()
通过MAE比较可以发现单步模型中,DNN的效果最佳
mae_val = [v[1] for v in val_performance.values()]
mae_test = [v[1] for v in performance.values()]
x = np.arange(len(performance))
fig, ax = plt.subplots()
ax.bar(x - 0.15, mae_val, width=0.25, color='black', edgecolor='black', label='Validation')
ax.bar(x + 0.15, mae_test, width=0.25, color='white', edgecolor='black', hatch='/', label='Test')
ax.set_ylabel('Mean absolute error')
ax.set_xlabel('Models')
for index, value in enumerate(mae_val):
plt.text(x=index - 0.15, y=value + 0.0025, s=str(round(value, 3)), ha='center')
for index, value in enumerate(mae_test):
plt.text(x=index + 0.15, y=value + 0.0025, s=str(round(value, 3)), ha='center')
plt.ylim(0, 0.1)
plt.xticks(ticks=x, labels=performance.keys())
plt.legend(loc='best')
plt.tight_layout()
plt.show()
代码地址:https://download.csdn.net/download/HughYue1990/86743129
数据集下载地址: https://download.csdn.net/download/HughYue1990/86744775