在构建可训练的模型之前,最好先设立性能基准以与之后更复杂的模型进行比较。这里的首要任务为基于当前所有的特征值对未来一小时的温度进行预测,这里的特征值可包含当前的温度。由于温度变化缓慢,将仅返回当前温度作为预测值的模型作为基准,是一个合理的假设。当然,如果要做进一步的预测,这个基准模型将难以发挥作用。
classBaseline(tf.keras.Model):def__init__(self,label_index=None):super().__init__()self.label_index=label_indexdefcall(self,inputs):ifself.label_indexisNone:returninputsresult=inputs[:,:,self.label_index]returnresult[:,:,tf.newaxis]
实例化并评估此模型:
baseline=Baseline(label_index=column_indices['T (degC)'])baseline.compile(loss=tf.losses.MeanSquaredError(),metrics=[tf.metrics.MeanAbsoluteError()])val_performance={}performance={}val_performance['Baseline']=baseline.evaluate(single_step_window.val)performance['Baseline']=baseline.evaluate(single_step_window.test,verbose=0)# 178/178 [==============================] - 1s 6ms/step - loss: 0.0196 - mean_absolute_error: 0.1022
这里打印了一些性能指标,但这些指标并没有使你对模型的运行情况有任何感觉。WindowGenerator有一个plot方法,但只绘制一个样本并没有太大意思。因此,让我们创建一个更大的WindowGenerator,一次生成窗口宽度为24小时的连续输入和标签。wide_window并不会改变模型的运行方式,其仍基于单个输入时间步对未来1小时进行预测。在这里,时间轴的作用类似于批处理轴:每个预测都是独立进行的,每个时间步之间没有交互。
wide_window=WindowGenerator(input_width=24,label_width=24,shift=1,label_columns=['T (degC)'])wide_window# Total window size: 25# Input indices: [ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23]# Label indices: [ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24]# Label column name(s): ['T (degC)']
这个扩展窗口可以直接传递给相同的基准模型,而无需更改任何代码。这是因为输入和标签具有相同的时间步长,基准仅将输入转发到输出:
print('输入形状:',wide_window.example[0].shape)print('输出形状:',baseline(wide_window.example[0]).shape)# 输入形状: (32, 24, 19)# 输出形状: (32, 24, 1)
绘制基准模型的预测,你可以看到它只是把标签往右偏移了1小时。
wide_window.plot(baseline)
在上面的三个示例图中,单步预测模型运行了24小时,我们需要做一些解释:
蓝色线段显示了每个时间步的输入温度,实际上模型接收了所有的特征,这个示例中我们只展示了温度;绿色标签点显示了目标观测值,这些点显示在预测时间而非输入时间;橙色的交叉点是每个时间步模型的预测值,如果模型能够完美地进行预测,那么这些预测将直接落在标签点上。

4.2. 线性模型(Linear)

可以应用于此任务的最简单的可训练模型是在输入和输出之间插入线性变换。 在这种情况下,每步的输出仅取决于该时间步:
一个没有激活函数的layer.Dense是线性模型。该线性层仅将数据的最后一个轴从(batch, time, inputs)转换为(batch, time, units),它独立应用于批和时间轴上的每一项。
linear=tf.keras.Sequential([tf.keras.layers.Dense(units=1)])print('输入形状:',single_step_window.example[0].shape)print('输出形状:',linear(single_step_window.example[0]).shape)# 输入形状: (32, 1, 19)# 输出形状: (32, 1, 1)
由于本教程将会训练多种模型,我们将训练过程打包成一个函数:
MAX_EPOCHS=20defcompile_and_fit(model,window,patience=2):early_stopping=tf.keras.callbacks.EarlyStopping(monitor='val_loss',patience=patience,mode='min')model.compile(loss=tf.losses.MeanSquaredError(),optimizer=tf.optimizers.Adam(),metrics=[tf.metrics.MeanAbsoluteError()])history=model.fit(window.train,epochs=MAX_EPOCHS,validation_data=window.val,callbacks=[early_stopping])returnhistory
训练模型并评估其性能:
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)# Epoch 1/20# 622/622 [==============================] - 5s 8ms/step - loss: 0.5126 - mean_absolute_error: 0.5389 - val_loss: 0.0432 - val_mean_absolute_error: 0.1667# Epoch 2/20# 622/622 [==============================] - 4s 6ms/step - loss: 0.0262 - mean_absolute_error: 0.1239 - val_loss: 0.0173 - val_mean_absolute_error: 0.1001# Epoch 3/20# 622/622 [==============================] - 4s 6ms/step - loss: 0.0146 - mean_absolute_error: 0.0909 - val_loss: 0.0136 - val_mean_absolute_error: 0.0862# ......
像基准模型一样,线性模型可以在批量的宽窗口中调用。通过这个方式,模型可以在连续的时间步上进行一组独立的预测。时间轴的表现如同另一个批处理轴。每个时间步的预测之间没有发生交互。
print('输入形状:',wide_window.example[0].shape)print('输出形状:',baseline(wide_window.example[0]).shape)# 输入形状: (32, 24, 19)# 输出形状: (32, 24, 1)
下图是线性模型在wide_window上的预测示例图。在大多数情况下,其预测明显比仅返回输入温度要好,但在某些情况下,结果较差:
线性模型的优点之一是它们的解释相对简单,你可以抽取线性层的权重,并查看分配给每个输入的权重:
plt.bar(x=range(len(train_df.columns)),height=linear.layers[0].kernel[:,0].numpy())axis=plt.gca()axis.set_xticks(range(len(train_df.columns)))_=axis.set_xticklabels(train_df.columns,rotation=90)
从上图可以看出,模型甚至没有将最大权重放在输入T (degC)上,这是随机初始化权重的风险之一。

4.3. 稠密连接(Dense)

在应用可在多个时间步上运行的模型之前,我们可以检视更深、功能更强的单输入模型性能。下面的模型类似于线性模型,不同之处在于它在输入和输出之间堆叠了若干个Dense层:
dense=tf.keras.Sequential([tf.keras.layers.Dense(units=64,activation='relu'),tf.keras.layers.Dense(units=64,activation='relu'),tf.keras.layers.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)# Epoch 1/20# 622/622 [==============================] - 6s 9ms/step - loss: 0.0804 - mean_absolute_error: 0.1589 - val_loss: 0.0109 - val_mean_absolute_error: 0.0785# Epoch 2/20# 622/622 [==============================] - 5s 8ms/step - loss: 0.0100 - mean_absolute_error: 0.0720 - val_loss: 0.0098 - val_mean_absolute_error: 0.0726# Epoch 3/20# 622/622 [==============================] - 4s 7ms/step - loss: 0.0091 - mean_absolute_error: 0.0684 - val_loss: 0.0100 - val_mean_absolute_error: 0.0753# ......

4.4. 多步稠密连接(Multi-step dense)

单步模型不包含当前输入值的上下文,因而其观察不到输入特征随时间变化的情况。为了解决这个问题,模型在进行预测时需要访问多个时间步:
baseline、linear和dense模型是独立处理每个时间步的,而此模型将采用多个时间步作为输入,并产生单个输出。创建一批有3小时输入和1小时标签的WindowGenerator:
CONV_WIDTH=3conv_window=WindowGenerator(input_width=CONV_WIDTH,label_width=1,shift=1,label_columns=['T (degC)'])conv_window# Total window size: 4# Input indices: [0 1 2]# Label indices: [3]# Label column name(s): ['T (degC)']conv_window.plot()plt.title("基于前3小时输入预测未来1小时的标签")
你可以通过添加layers.Flatten作为模型的第一层来训练多输入窗口的dense模型:
multi_step_dense=tf.keras.Sequential([# 形状: (时间步, 特征数) => (时间 * 特征数)tf.keras.layers.Flatten(),tf.keras.layers.Dense(units=32,activation='relu'),tf.keras.layers.Dense(units=32,activation='relu'),tf.keras.layers.Dense(units=1),# 重新添加时间轴# 形状: (输出数目) => (1, 输入数目)tf.keras.layers.Reshape([1,-1]),])print('输入形状:',conv_window.example[0].shape)print('输出形状:',multi_step_dense(conv_window.example[0]).shape)# 输入形状: (32, 3, 19)# 输出形状: (32, 1, 1)history=compile_and_fit(multi_step_dense,conv_window)IPython.display.clear_output()val_performance['Multi step dense']=multi_step_dense.evaluate(conv_window.val)performance['Multi step dense']=multi_step_dense.evaluate(conv_window.test,verbose=0)# 178/178 [==============================] - 1s 3ms/step - loss: 0.0095 - mean_absolute_error: 0.0712conv_window.plot(multi_step_dense)
这种方法的主要缺点是,只能在具有这种形状的输入窗口上执行生成的模型。

4.5. 卷积神经网络(Convolution neural network)

卷积层layers.Conv1D同样可以将多个时间步作为每个预测的输入,下面我们在multi_step_dense的基础上重写代码, 不同之处在于:
使用layers.Conv1D取代layers.Flatten和第一个layers.Dense;由于卷积运算可在输出中保留时间轴,无需再使用layers.Reshape。
conv_model=tf.keras.Sequential([tf.keras.layers.Conv1D(filters=32,kernel_size=(CONV_WIDTH,),activation='relu'),tf.keras.layers.Dense(units=32,activation='relu'),tf.keras.layers.Dense(units=1),])
在示例批处理上运行它,查看模型输出的形状是否符合我们的预期:
print("`conv_window`上的卷积模型")print('输入形状: ',conv_window.example[0].shape)print('输出形状: ',conv_model(conv_window.example[0]).shape)# `conv_window`上的卷积模型# 输入形状: (32, 3, 19)# 输出形状: (32, 1, 1)
然后在conv_window上训练并评估模型,它的性能应该和multi_step_dense模型相近。
history=compile_and_fit(conv_model,conv_window)IPython.display.clear_output()val_performance['Conv']=conv_model.evaluate(conv_window.val)performance['Conv']=conv_model.evaluate(conv_window.test,verbose=0)# 178/178 [==============================] - 1s 6ms/step - loss: 0.0094 - mean_absolute_error: 0.0717
conv_model和multi_step_dense模型之间的区别在于conv_model可以在任意长度的输入上运行,卷积层可以应用于一个滑动的输入窗口:
如果你提供更宽的输入,那么模型也会产生更宽的输出:
print('输入形状:',wide_window.example[0].shape)print('标签形状:',wide_window.example[1].shape)print('输出形状:',conv_model(wide_window.example[0]).shape)# 输入形状: (32, 24, 19)# 标签形状: (32, 24, 1)# 输出形状: (32, 22, 1)
需要注意的是,输出比输入要窄,如果要进行训练或绘图,需要标签和预测具有相同的长度。因此,需要构建一个带有一些额外输入时间步,便于标签和预测长度匹配的WindowGenerator:
LABEL_WIDTH=24INPUT_WIDTH=LABEL_WIDTH+(CONV_WIDTH-1)wide_conv_window=WindowGenerator(input_width=INPUT_WIDTH,label_width=LABEL_WIDTH,shift=1,label_columns=['T (degC)'])wide_conv_window# Total window size: 27# Input indices: [ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25]# Label indices: [ 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26]# Label column name(s): ['T (degC)']print('输入形状:',wide_window.example[0].shape)print('标签形状:',wide_window.example[1].shape)print('输出形状:',conv_model(wide_window.example[0]).shape)# 输入形状: (32, 24, 19)# 标签形状: (32, 24, 1)# 输出形状: (32, 22, 1)
现在,您可以在更宽的窗口上绘制模型的预测。注意此处每个预测都基于前面的3个时间步:
wide_conv_window.plot(conv_model)

4.6. 循环神经网络(Recurrent neural network)

循环神经网络(RNN)是一种非常适合时间序列数据的神经网络。它分步处理时间序列,从一个时间步切换到下一时间步均保持内部状态。本教程中,我们会使用一个叫长短期记忆(Long Short Term Memory)的RNN层。所有keras RNN层中最重要的构造参数为return_sequences参数,可以通过一下两种方式设置构造RNN层:
1、如果默认为False,则该层仅返回最终时间步的输出,从而在进行单个预测之前给模型时间预热其内部状态:
2、如果为True,该层返回每个输入的输出,有利于:
堆叠RNN层;同时在多个时间步上训练模型。
lstm_model=tf.keras.models.Sequential([# 形状 [批量数, 时间步, 特征数] => [批量数, 时间步, 单元数]tf.keras.layers.LSTM(32,return_sequences=True),# 形状 => [批量数, 时间步, 特征数]tf.keras.layers.Dense(units=1)])
通过设置return_sequences = True,模型可以一次训练24小时的数据。
print('输入形状:',wide_window.example[0].shape)print('输出形状:',lstm_model(wide_window.example[0]).shape)# 输入形状: (32, 24, 19)# 输出形状: (32, 24, 1)history=compile_and_fit(lstm_model,wide_window)IPython.display.clear_output()val_performance['LSTM']=lstm_model.evaluate(wide_window.val)performance['LSTM']=lstm_model.evaluate(wide_window.test,verbose=0)# 177/177 [==============================] - 1s 6ms/step - loss: 0.0075 - mean_absolute_error: 0.0616wide_window.plot(lstm_model)

4.7. 模型性能

此数据集中,通常每个模型的性能都比之前的模型好一些。
x=np.arange(len(performance))width=0.3metric_name='mean_absolute_error'metric_index=lstm_model.metrics_names.index('mean_absolute_error')val_mae=[v[metric_index]forvinval_performance.values()]test_mae=[v[metric_index]forvinperformance.values()]plt.ylabel('平均绝对误差 [T (degC), normalized]')plt.bar(x-0.17,val_mae,width,label='Validation')plt.bar(x+0.17,test_mae,width,label='Test')plt.xticks(ticks=x,labels=performance.keys(),rotation=45)_=plt.legend()

4.8. 多输出模型

到目前为止,所有模型仅针对单个时间步预测了单个输出特征T (degC)。通过更改输出层中的单位数量并调整训练窗口将所有特征包含在标签中,便可将这些模型转换为多输出模型。
single_step_window=WindowGenerator(# 如果不设置`label_columns`参数,`WindowGenerator`会返回所有特征input_width=1,label_width=1,shift=1)wide_window=WindowGenerator(input_width=24,label_width=24,shift=1)forexample_inputs,example_labelsinwide_window.train.take(1):print(f'输入形状 (批量数, 时间步, 特征数): {example_inputs.shape}')print(f'标签形状 (批量数, 时间步, 特征数): {example_labels.shape}')# 输入形状 (批量数, 时间步, 特征数): (32, 24, 19)# 标签形状 (批量数, 时间步, 特征数): (32, 24, 19)
4.8.1. 基准模型
此处可以使用相同的基准模型,但这次重复所有特征,而非选择特定的label_index。
baseline=Baseline()baseline.compile(loss=tf.losses.MeanSquaredError(),metrics=[tf.metrics.MeanAbsoluteError()])val_performance={}performance={}val_performance['Baseline']=baseline.evaluate(wide_window.val)performance['Baseline']=baseline.evaluate(wide_window.test,verbose=0)# 177/177 [==============================] - 1s 6ms/step - loss: 0.0935 - mean_absolute_error: 0.1743
4.8.2. 稠密连接
dense=tf.keras.Sequential([tf.keras.layers.Dense(units=64,activation='relu'),tf.keras.layers.Dense(units=64,activation='relu'),tf.keras.layers.Dense(units=num_features)])history=compile_and_fit(dense,single_step_window)IPython.display.clear_output()val_performance['Dense']=dense.evaluate(single_step_window.val)performance['Dense']=dense.evaluate(single_step_window.test,verbose=0)# 178/178 [==============================] - 1s 5ms/step - loss: 0.0752 - mean_absolute_error: 0.1492
4.8.3. 循环神经网络
%%timewide_window=WindowGenerator(input_width=24,label_width=24,shift=1)lstm_model=tf.keras.models.Sequential([# 形状 [批量数, 时间步, 特征数] => [批量数, 时间步, 单元数]tf.keras.layers.LSTM(32,return_sequences=True),# 形状 => [批量数, 时间步, 特征数]tf.keras.layers.Dense(units=num_features)])history=compile_and_fit(lstm_model,wide_window)IPython.display.clear_output()val_performance['LSTM']=lstm_model.evaluate(wide_window.val)performance['LSTM']=lstm_model.evaluate(wide_window.test,verbose=0)print()# 177/177 [==============================] - 1s 5ms/step - loss: 0.0653 - mean_absolute_error: 0.1323# Wall time: 2min 6s
4.8.4. 进阶:残差连接
之前的Baseline模型利用了以下事实:序列不会随时间急剧变化。到目前为止,本教程中训练的每个模型都是随机初始化的,然后必须知道输出与上个时间步相比仅有微笑变化。尽管可以通过精心设计初始化来解决这个问题,但将其构建到模型结构中会更为简单。
在时间序列分析中,建立的模型通常用来预测下一个时间步中值将如何变化而非预测下一个值。类似地,深度学习中的“残差网络”在每一层中增加了模型的累加结果,这样就可以利用相邻时间步差值不大这一知识。
本质上,这将初始化模型以匹配基准。 对于此任务,它可以帮助模型更快地收敛,并具有更好的性能。该方法可以与本教程中讨论的任何模型一起使用。在这里将其应用于LSTM模型,请注意tf.initializers.zeros的使用,以确保初始的预测变化很小,并且不会覆盖的连接。 由于零仅用于最后一层,因此这里的梯度没有破坏对称性的问题。
classResidualWrapper(tf.keras.Model):def__init__(self,model):super().__init__()self.model=modeldefcall(self,inputs,*args,**kwargs):delta=self.model(inputs,*args,**kwargs)# 每个时间步长的预测是前一个时间步长的输入加上模型计算的增量returninputs+delta%%timeresidual_lstm=ResidualWrapper(tf.keras.Sequential([tf.keras.layers.LSTM(32,return_sequences=True),tf.keras.layers.Dense(num_features,# 预测的增量应从小开始,因此请使用零初始化输出层kernel_initializer=tf.initializers.zeros())]))history=compile_and_fit(residual_lstm,wide_window)IPython.display.clear_output()val_performance['Residual LSTM']=residual_lstm.evaluate(wide_window.val)performance['Residual LSTM']=residual_lstm.evaluate(wide_window.test,verbose=0)print()# 177/177 [==============================] - 1s 7ms/step - loss: 0.0653 - mean_absolute_error: 0.1280# Wall time: 38.3 s
4.8.5. 模型性能
x=np.arange(len(performance))width=0.3metric_name='mean_absolute_error'metric_index=lstm_model.metrics_names.index('mean_absolute_error')val_mae=[v[metric_index]forvinval_performance.values()]test_mae=[v[metric_index]forvinperformance.values()]plt.bar(x-0.17,val_mae,width,label='Validation')plt.bar(x+0.17,test_mae,width,label='Test')plt.xticks(ticks=x,labels=performance.keys(),rotation=45)plt.ylabel('平均绝对误差 (所有输出上的平均值)')_=plt.legend()

5. 多时间步模型

前面几节中的单输出和多输出模型都对未来的1小时进行了单步预测,本节介绍如何扩展这些模型以进行多时间步预测。在多时间步预测中,模型需要预测一系列未来的值。因此,与单步预测不同,多时间步模型预测未来值的序列。一般有两种方法:
一次预测整个时间序列的单发预测;仅进行单步预测并将其输出作为输入反馈的自回归预测。
对于多时间步模型,训练数据同样由每小时样本组成,但其基于过去的24小时来学会预测未来的24小时。让我们从数据集中创建一个WindowGenerator对象:
OUT_STEPS=24multi_window=WindowGenerator(input_width=24,label_width=OUT_STEPS,shift=OUT_STEPS)multi_window.plot()multi_window# Total window size: 48# Input indices: [ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23]# Label indices: [24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47]# Label column name(s): None

5.1. 基准模型

同样地,我们将仅返回上个输入的模型作为基准:
classMultiStepLastBaseline(tf.keras.Model):defcall(self,inputs):returntf.tile(inputs[:,-1:,:],[1,OUT_STEPS,1])last_baseline=MultiStepLastBaseline()last_baseline.compile(loss=tf.losses.MeanSquaredError(),metrics=[tf.metrics.MeanAbsoluteError()])multi_val_performance={}multi_performance={}multi_val_performance['Last']=last_baseline.evaluate(multi_window.val)multi_performance['Last']=last_baseline.evaluate(multi_window.test,verbose=0)multi_window.plot(last_baseline)# 176/176 [==============================] - 1s 5ms/step - loss: 0.7086 - mean_absolute_error: 0.5547
由于此任务是在给定24小时的情况下预测未来24小时,因此另一种简单的方法是假设明天的情况和今天一样:
classRepeatBaseline(tf.keras.Model):defcall(self,inputs):returninputsrepeat_baseline=RepeatBaseline()repeat_baseline.compile(loss=tf.losses.MeanSquaredError(),metrics=[tf.metrics.MeanAbsoluteError()])multi_val_performance['Repeat']=repeat_baseline.evaluate(multi_window.val)multi_performance['Repeat']=repeat_baseline.evaluate(multi_window.test,verbose=0)multi_window.plot(repeat_baseline)# 176/176 [==============================] - 1s 6ms/step - loss: 0.4219 - mean_absolute_error: 0.4038

5.2. 单发预测模型

实现多时间步预测的一种高级方法是使用单发模型,该模型可以一次完成整个序列的预测。这可以通过设置layers.Dense的输出单元数为OUT_STEPS*features高效实现。模型只需要将输出的形状重塑为(OUT_STEPS, features)即可。
5.2.1. 线性模型
基于最后输入时间步的简单线性模型比任一基准模型要好,但性能较弱。 该模型需要根据线性投影的单个时间步来预测OUT_STEPS个时间步。它只能捕获数据的低维行为,如一天中的时间和一年中的时间。
multi_linear_model=tf.keras.Sequential([# Take the last time-step.# Shape [batch, time, features] => [batch, 1, features]tf.keras.layers.Lambda(lambdax:x[:,-1:,:]),# Shape => [batch, 1, out_steps*features]tf.keras.layers.Dense(OUT_STEPS*num_features,kernel_initializer=tf.initializers.zeros()),# Shape => [batch, out_steps, features]tf.keras.layers.Reshape([OUT_STEPS,num_features])])history=compile_and_fit(multi_linear_model,multi_window)IPython.display.clear_output()multi_val_performance['Linear']=multi_linear_model.evaluate(multi_window.val)multi_performance['Linear']=multi_linear_model.evaluate(multi_window.test,verbose=0)multi_window.plot(multi_linear_model)# 176/176 [==============================] - 1s 4ms/step - loss: 0.2624 - mean_absolute_error: 0.3223
5.2.2. 稠密连接
在输入和输出之间添加layers.Dense可以增强模型,但仍仅基于单个时间步输入。
multi_dense_model=tf.keras.Sequential([# Take the last time step.# Shape [batch, time, features] => [batch, 1, features]tf.keras.layers.Lambda(lambdax:x[:,-1:,:]),# Shape => [batch, 1, dense_units]tf.keras.layers.Dense(512,activation='relu'),# Shape => [batch, out_steps*features]tf.keras.layers.Dense(OUT_STEPS*num_features,kernel_initializer=tf.initializers.zeros()),# Shape => [batch, out_steps, features]tf.keras.layers.Reshape([OUT_STEPS,num_features])])history=compile_and_fit(multi_dense_model,multi_window)IPython.display.clear_output()multi_val_performance['Dense']=multi_dense_model.evaluate(multi_window.val)multi_performance['Dense']=multi_dense_model.evaluate(multi_window.test,verbose=0)multi_window.plot(multi_dense_model)# 176/176 [==============================] - 1s 5ms/step - loss: 0.2347 - mean_absolute_error: 0.3042
5.2.3. 卷积神经网络
卷积神经网络基于固定宽度的历史记录进行预测,这可能比Dense模型具有更好的性能,因为它可以看到事物随时间的变化:
CONV_WIDTH=3multi_conv_model=tf.keras.Sequential([# Shape [batch, time, features] => [batch, CONV_WIDTH, features]tf.keras.layers.Lambda(lambdax:x[:,-CONV_WIDTH:,:]),# Shape => [batch, 1, conv_units]tf.keras.layers.Conv1D(256,activation='relu',kernel_size=(CONV_WIDTH)),# Shape => [batch, 1, out_steps*features]tf.keras.layers.Dense(OUT_STEPS*num_features,kernel_initializer=tf.initializers.zeros()),# Shape => [batch, out_steps, features]tf.keras.layers.Reshape([OUT_STEPS,num_features])])history=compile_and_fit(multi_conv_model,multi_window)IPython.display.clear_output()multi_val_performance['Conv']=multi_conv_model.evaluate(multi_window.val)multi_performance['Conv']=multi_conv_model.evaluate(multi_window.test,verbose=0)multi_window.plot(multi_conv_model)# 176/176 [==============================] - 1s 5ms/step - loss: 0.2315 - mean_absolute_error: 0.3049
5.2.4. 循环神经网络
如果循环神经网络与模型所做的预测相关,则可以学习使用较长的输入。该模型将在24小时内累积内部状态,然后对下一个24小时进行一次预测。在这种单发格式中,LSTM只需要在最后一个时间步产生输出,因此需要设置return_sequences = False。
multi_lstm_model=tf.keras.Sequential([# Shape [batch, time, features] => [batch, lstm_units]# Adding more `lstm_units` just overfits more quickly.tf.keras.layers.LSTM(32,return_sequences=False),# Shape => [batch, out_steps*features]tf.keras.layers.Dense(OUT_STEPS*num_features,kernel_initializer=tf.initializers.zeros()),# Shape => [batch, out_steps, features]tf.keras.layers.Reshape([OUT_STEPS,num_features])])history=compile_and_fit(multi_lstm_model,multi_window)IPython.display.clear_output()multi_val_performance['LSTM']=multi_lstm_model.evaluate(multi_window.val)multi_performance['LSTM']=multi_lstm_model.evaluate(multi_window.test,verbose=0)multi_window.plot(multi_lstm_model)# 176/176 [==============================] - 1s 4ms/step - loss: 0.2312 - mean_absolute_error: 0.3162

5.3. 进阶:自回归模型

上面的模型都可以在一个步骤中预测整个输出序列。在某些情况下,模型将预测分解成各个时间步可能会有所帮助。然后,每个模型的输出都可以在每个步骤中反馈给自己,并且可以在前一个模型的基础上进行预测,就像在经典的循环神经网络生成序列中一样。这种模型样式的一个明显优势是可以将其输出的长度可变。你可以采用本教程前半部分中训练的任何单步多输出模型,并在自回归反馈循环中运行,但是在这里,我们将集中精力构建经过显式训练的模型。