本文将展示一种新的时间序列预测方法。
目标数据集
在这个项目中使用的数据是来自北卡罗来纳州夏洛特分校的全球能源预测竞赛的数据。您可以在这里找到更多信息:http://www.drhongtao.com/gefcom/2017
你需要知道的是,这些数据是来自能源网络的各种读数。我们的目标是利用这些数据点预测电网的实时能源需求。数据点还包括露点和干球温度,因为空调是能源消耗的主力。
我们的目标变量是RTDemand(Real Time energy demand):电网的实时能源需求。数据具有清晰的日周期特征。以下是我们三天的数据:
三天内的每小时数据
在每个人都在睡觉的半夜里,我们的耗电量达到最低。我们早上醒来,开始工作,当太阳达到峰值强度时,我们的能量消耗达到了最大值。因此可以认为每天的能耗下降与通勤时间相对应。
如果我们再把尺度放大一些,我们可以看到清晰的自相关特性和日趋势。以下是大约3周的数据:
三周内的每小时数据
我们还可以注意到一个更大的季节性趋势,如果我们进一步缩小并查看一整年的数据:
一年内的每小时数据
由此看见,这是一个相当理想的时间序列数据,可以对其进行预测。
单变量纯时间序列预测模型
对于时间序列预测,我们将需要给定一个目标结果的时间序列。在我们的例子中,我选择72小时作为时间序列的长度。这意味着我们模型的输入是72个单独的数字,代表过去72小时的数据,我们希望从模型中得到的目标输出是它对第73小时的预测。我认为72小时是一个很好的长度,因为它可以很好地捕捉当地的趋势和昼夜循环。
以下是我们对模型的输入(连续三天的数据):
array([ [12055., 11430., 10966., 10725., 10672., 10852., 11255., 11583., 12238., 12877., 13349., 13510., 13492., 13314., 13156., 13364., 14632., 15653., 15504., 15088., 14579., 13882., 12931., 11883., 10978., 10406., 10089., 9982., 10031., 10289., 10818., 11444., 12346., 13274., 13816., 14103., 14228., 14154., 14055., 14197., 15453., 16531., 16410., 15954., 15337., 14347., 13178., 12106., 11400., 11059., 10959., 11073., 11485., 12645., 14725., 15863., 16076., 16222., 16358., 16362., 16229., 16123., 15976., 16127., 17359., 18818., 18724., 18269., 17559., 16383., 14881., 13520.], [11430., 10966., 10725., 10672., 10852., 11255., 11583., 12238., 12877., 13349., 13510., 13492., 13314., 13156., 13364., 14632., 15653., 15504., 15088., 14579., 13882., 12931., 11883., 10978., 10406., 10089., 9982., 10031., 10289., 10818., 11444., 12346., 13274., 13816., 14103., 14228., 14154., 14055., 14197., 15453., 16531., 16410., 15954., 15337., 14347., 13178., 12106., 11400., 11059., 10959., 11073., 11485., 12645., 14725., 15863., 16076., 16222., 16358., 16362., 16229., 16123., 15976., 16127., 17359., 18818., 18724., 18269., 17559., 16383., 14881., 13520., 12630.], [10966., 10725., 10672., 10852., 11255., 11583., 12238., 12877., 13349., 13510., 13492., 13314., 13156., 13364., 14632., 15653., 15504., 15088., 14579., 13882., 12931., 11883., 10978., 10406., 10089., 9982., 10031., 10289., 10818., 11444., 12346., 13274., 13816., 14103., 14228., 14154., 14055., 14197., 15453., 16531., 16410., 15954., 15337., 14347., 13178., 12106., 11400., 11059., 10959., 11073., 11485., 12645., 14725., 15863., 16076., 16222., 16358., 16362., 16229., 16123., 15976., 16127., 17359., 18818., 18724., 18269., 17559., 16383., 14881., 13520., 12630., 12223.] ])
输入数组中的每一个数字都是RTDemand的读数:这个特定的发电站每小时需要多少千瓦的电力。每个数组中都有72个小时的数据。如果你仔细观察这3个数组中前8个左右的读数,你会注意到每一个新的数组都是一个向前移动了1小时的序列。因此,这72个长度的输入数组中的每一个数据都代表了最后72小时对这个能源网实时需求的读数。
我们需要预测第73小时的需求,所以目标数组格式如下:
array([[12630.], [12223.], [12070.]])
需要注意的是,目标数组中的第一个数据是输入数组中第二个数组的最后一个数据,目标数组中的第二个数据是输入数组中第三个数组的最后一个数据。也就是说,我们通过输入数组中的第一个数组来实现对于目标数组中第一个数据的预测。
数据转换
一旦我们加载了数据,接下来我们需要将其转换成一个适当的数据集,用于训练机器学习模型。首先,缩放所有输入变量。稍后,我们将讨论如何使用数据集的所有12个输入,但现在将只使用1个变量作为输入,以便于介绍本文使用的预测方法。本文不会对目标变量Y进行缩放处理,因为它可以使监控模型的进度变得更容易,成本最低。接下来,我们将把数据分为一个训练集和一个测试集:
fromsklearn.preprocessingimportStandardScalerscaler=StandardScaler() X=scaler.fit_transform(X) split=int(0.8*len(X)) X_train=X[: split-1] X_test=X[split:] y_train=y[: split-1] y_test=y[split:]
最后,我们将使用的模型的输入是(Samples、Timesteps、Features)。在第一个模型中,我们只使用时间窗口的目标变量作为输入。所以,我们只有一个输入特征Feature。我们的输入即为(Samples,Timesteps)。在进行训练集和测试集分割之后,我们现在将对其进行reshape处理:
X_train=X_train.reshape((X_train.shape[0], X_train.shape[1], 1)) X_test=X_test.reshape((X_test.shape[0], X_test.shape[1], 1)) X_train.shape(61875, 72, 1)
也就是说,第一个模型的输入数据为61875个样本、每个样本包含有72小时数据以及1个特征。
基线模型
首先,我们建立一个基线模型。我们的优化函数设置为均方误差/均方根误差。我们同时也监测R²,不过,如果存在冲突,我们只使用均方误差作为损失函数和优化目标。
对于基线模型,我们将看到均方误差和R²的数据情况。这里的基准模型实现的功能是猜测时间序列中先前的值。下面是相关代码:
#Benchmarkmodelprev_val=y_test[0] sse=0forninrange(0, len(y_test)-1): err=y_test[n] —prev_valsq_err=err**2sse=sse+sq_errprev_val=y_test[n] mse=sse/nmse
使用我们的测试数据集,得到的平方根误差是641.54。也就是说,这个基准模型在给定的一小时内会相对于真实情况相差641.54兆瓦。这是基准模型与实际结果的图表:
真实数据曲线&基准模型预测曲线
虽然第一个模型很简单,但是性能上表现出的效果良好。接下来我们尝试其它的模型方法。
LSTM预测模型
时间序列数据预测常用的模型之一就是LSTM。相对于本文提出的卷积预测模型,它是一个很有意义的对照模型。LSTM预测的相关代码如下:
defbasic_LSTM(window_size=5, n_features=1): new_model=keras.Sequential() new_model.add(tf.keras.layers.LSTM(100, input_shape=(window_size, n_features), return_sequences=True, activation=’relu’)) new_model.add(tf.keras.layers.Flatten()) new_model.add(tf.keras.layers.Dense(1500, activation=’relu’)) new_model.add(tf.keras.layers.Dense(100, activation=’linear’)) new_model.add(tf.keras.layers.Dense(1)) new_model.compile(optimizer=”adam”, loss=”mean_squared_error”) returnnew_modells_model=basic_LSTM(window_size=window_size, n_features=X_train.shape[2]) ls_model.summary() Model: "sequential"_________________________________________________________________Layer (type) OutputShapeParam#=================================================================lstm (LSTM) (None, 72, 100) 40800_________________________________________________________________flatten (Flatten) (None, 7200) 0_________________________________________________________________dense (Dense) (None, 1500) 10801500_________________________________________________________________dense_1 (Dense) (None, 100) 150100_________________________________________________________________dense_2 (Dense) (None, 1) 101=================================================================Totalparams: 10,992,501Trainableparams: 10,992,501Non-trainableparams: 0
通过训练数据集进行模型训练,然后通过测试集进行评价:
ls_model.evaluate(X_test, y_test, verbose=0) 1174830.0587427279fromsklearn.metricsimportr2_scorepredictions=ls_model.predict(X_test) test_r2=r2_score(y_test, predictions) test_r20.8451637094740732
我们得到的结果不是很好。具体地说,我们最终得到的误差比之前的基准模型更高。下面的图表可以了解它的预测情况:LSTM预测结果
正如上图所示,LSTM的预测具有较大的不确定性。