时间序列预测14:CNN 实现用电量/发电量预测

2023-10-18 16:30

本文主要是介绍时间序列预测14:CNN 实现用电量/发电量预测,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!


【时间序列预测/分类】 全系列60篇由浅入深的博文汇总:传送门


文章目录

  • 前言
  • 适用于多时间步预测的CNN模型
  • 1 单变量多步预测 CNN 模型
    • 1.1 业务需求
    • 1.2 1D CNN 模型
    • 1.3 完整代码


前言

与其他机器学习算法不同,卷积神经网络能够从序列数据中自动学习特征,支持多变量数据,并可直接输出用于多步预测的向量。一维CNN已被证明可以很好地执行,甚至在具有挑战性的序列预测问题上也能达到最新的结果。

计划用两篇文章介绍如何开发 1D CNN 进行多步时间序列预测。主要内容如下:

  • 如何为单变量数据开发多步时间序列预测的CNN模型;
  • 如何为多变量数据开发多通道多步时间序列预测的CNN模型;
  • 如何为多变量数据开发多头多步时间序列预测的CNN模型。

本文介绍如何为单变量数据开发多步时间序列预测的CNN模型。


代码环境:

  • python 3.7.6
  • tensorflow 2.1.0
  • keras 2.3.1

代码在 jupyter notebook编写。完整代码部分对比较难理解的地方做了注释,应该很容易理解。


适用于多时间步预测的CNN模型

数据处理和模型评估部分之前的文章已经介绍过了,本文不再赘述。之前的文章:传送门


卷积神经网络(CNN)可用于多步时间序列预测。CNN也可用于递归或直接预测策略,在该策略中,模型进行一步预测,并为每个要预测的时间步开发一个模型。或者,CNN可以用来预测整个输出序列,作为整个矢量的一步预测。这是前馈神经网络的普遍优势。使用CNN的一个重要好处是:支持多个1D输入以进行预测。如果多步输出序列是一个以上输入序列的函数,则此功能很有用。这可以使用两种不同的模型配置来实现。

  • 多个输入通道(Multiple Input Channels):每个输入序列作为一个单独的通道读取,可类比图像的不同颜色通道(如RGB)。
  • 多个输入头(Multiple Input Heads):每个输入序列由不同的CNN子模型读取,并且模型的内部表征在解释之前先进行组合再进行预测。按个人理解,命名为子模型异构。

本文介绍三种业务场景下的CNN模型:

  • 单变量输入数据的多步时间序列预测CNN模型;
  • 多变量多输入通道的多步时间序列预测CNN模型;
  • 多变量子模型异构的多步时间序列预测CNN模型;

这些模型会在家庭用电量数据集上进行演示。通过之前的文章可知,如果一个模型比一个总RMSE约为465千瓦的七天预测朴素模型的RMSE小,就可以认为该模型是可用的。本文示例并没有调整超参数以获得最佳性能,所选择的结构和超参数都经过了小的尝试和误差。考虑到模型的随机性,最好对模型进行多次评估,并输出测试数据集的平均性能。


1 单变量多步预测 CNN 模型

1.1 业务需求

开发卷积神经网络进行多步时间序列预测,该预测使用上篇文章中转换好的日功耗单变量序列。业务需求:给定之前几天的总日耗电量,预测下一个标准周(周天开始,周六结束)的日耗电量。


1.2 1D CNN 模型

一维CNN模型要求输入数据的shape为:[样本,时间步长,特征]([samples, timesteps, features])。一个样本(sample)包含一周7天的日总有功功率,即滑动窗口的宽度为7;特征只有1个(原数据集有七个特征,不包含日期和时间),即7天的日总有功功率序列。训练数据集有159周的数据(将原数据集的每分钟采样一次的数据,重采样成每天采样数据,然后把前三年的作为训练集,最后一年的数据作为测试集,截取的都是完整周,丢弃不完整周的日数据。相关细节👓请看这里),因此训练集的shape为:[159,7,1]

将原数据集(每分钟采样一次)重采样成每天的数据,新数据的shape为:

(1442, 8)

这种格式的数据将使用之前的标准周来预测下一个标准周。问题是对于神经网络来说,159个训练样本是远远不够的。创建更多训练数据的一种方法是在训练期间做如下更改:根据之前的7天预测未来7天,而不考虑是否为标准周,可以理解滑动窗口的步长由7变为1,相当于增加了样本数(samples),特征数(features)不变,即行数增加,列数不变。而不是,相当于人为地在原数据集每两个样本之间又增加了6个新样本。再说详细一点就是,现在标准周预测数据相当于只有159行,2列,第一行是前一周的日总有功功率及期望预测输出(下一周的数据),第二行是之后一周的日总有功功率及期望输出预测(下下周的数据)…以此类推,最后行数(样本数变为159×7行)。原来的训练样本是使用当前一个标准周(周天开始,周六结束)的数据预测下一个标准周;使用下一个标准周预测下下个标准周…以此类推。而现在是放开标准周的限制来预测,比如从周一开始,周天结束(预测的总天数不变,同样是七天);周二开始,周一结束;周三开始,周二结束…以此类推。细说就是,由通过之前一周中,从周一开始到周天结束的非标准周的预测结果,相当于添加一行(一个样本);接着添加从周二开始到下周周一结束的非标准周预测结果,又相当于添加了一行…以此类推。测试集不做改变,仍然使用标准周进行预测。经过以上分析,训练数据的shape由 [159,7,1] 变为 [159*7,7,1]。其实最关键的还是段首说的那句话:滑动窗口的滑动步长由7变为1,相当于增加了样本数,特征数不变。画个图说明一下数据集扩充的思路:
在这里插入图片描述
代码实现:

def sliding_window(train, sw_width=7, in_start=0):'''该函数实现窗口宽度为7、滑动步长为1的滑动窗口截取序列数据'''data = train.reshape((train.shape[0] * train.shape[1], train.shape[2])) # 将以周为单位的样本展平为以天为单位的序列X, y = [], []for _ in range(len(data)):in_end = in_start + sw_widthout_end = in_end + sw_width# 保证截取样本完整,最大元素索引不超过原序列索引,则截取数据;否则丢弃该样本if out_end < len(data):# 训练数据以滑动步长1截取train_seq = data[in_start:in_end, 0]train_seq = train_seq.reshape((len(train_seq), 1))X.append(trian_seq)y.append(data[in_end:out_end], 0)in_start += 1return np.array(X), np.array(y)

在训练集上运行该函数,将159个样本转换为1099个样本。转换后,数据集的形状(shape)为 X=[1099, 7, 1]y=[1099, 7]。接下来,可以在训练数据上定义和拟合CNN模型。这个多步时间序列预测问题是一个自回归问题(只有一个特征,日总有功功率)。

较少的特征和数据意味着只需要一个简单的模型即能满足业务需求。定义参数配置为 filters=16kernel_size=3 的卷积层。这意味着7天的输入序列将通过卷积操作一次读取3个时间步长的值,该操作将执行16次。池化层把这些特征映射(feature maps)缩小1/4,之后将内部表示压缩为一个长向量。然后,全连接的层对其进行解释,最后在输出层输出序列预测结果。

使用均方误差损失函数(mse),因为它与我们选择的RMSE误差度量很好地匹配。使用随机梯度下降算法Adam,epochs=20,batch_size=4。该算法的小批量和随机性意味着同一个模型在每次训练时将学习输入到输出的略有不同的映射。这意味着评估模型时,结果可能会有所不同。可以尝试多次运行模型并计算模型性能的平均值。


1.3 完整代码

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt# 设置中文显示
plt.rcParams['font.sans-serif'] = ['Microsoft JhengHei']
plt.rcParams['axes.unicode_minus'] = Falseimport math
import sklearn.metrics as skm
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Flatten
from tensorflow.keras.layers import Conv1D,MaxPooling1Ddef split_dataset(data):'''该函数实现以周为单位切分训练数据和测试数据'''# data为按天的耗电量统计数据,shape为(1442, 8)# 测试集取最后一年的46周(322天)数据,剩下的159周(1113天)数据为训练集,以下的切片实现此功能。train, test = data[1:-328], data[-328:-6]train = np.array(np.split(train, len(train)/7)) # 将数据划分为按周为单位的数据test = np.array(np.split(test, len(test)/7))return train, testdef evaluate_forecasts(actual, predicted):'''该函数实现根据预期值评估一个或多个周预测损失思路:统计所有单日预测的 RMSE'''scores = list()for i in range(actual.shape[1]):mse = skm.mean_squared_error(actual[:, i], predicted[:, i])rmse = math.sqrt(mse)scores.append(rmse)s = 0 # 计算总的 RMSEfor row in range(actual.shape[0]):for col in range(actual.shape[1]):s += (actual[row, col] - predicted[row, col]) ** 2score = math.sqrt(s / (actual.shape[0] * actual.shape[1]))print('actual.shape[0]:{}, actual.shape[1]:{}'.format(actual.shape[0], actual.shape[1]))return score, scoresdef summarize_scores(name, score, scores):s_scores = ', '.join(['%.1f' % s for s in scores])print('%s: [%.3f] %s\n' % (name, score, s_scores))def sliding_window(train, sw_width=7, n_out=7, in_start=0):'''该函数实现窗口宽度为7、滑动步长为1的滑动窗口截取序列数据'''data = train.reshape((train.shape[0] * train.shape[1], train.shape[2])) # 将以周为单位的样本展平为以天为单位的序列X, y = [], []for _ in range(len(data)):in_end = in_start + sw_widthout_end = in_end + n_out# 保证截取样本完整,最大元素索引不超过原序列索引,则截取数据;否则丢弃该样本if out_end < len(data):# 训练数据以滑动步长1截取train_seq = data[in_start:in_end, 0]train_seq = train_seq.reshape((len(train_seq), 1))X.append(train_seq)y.append(data[in_end:out_end, 0])in_start += 1return np.array(X), np.array(y)def cnn_model(train, sw_width, in_start=0, verbose_set=0, epochs_num=20, batch_size_set=4):'''该函数定义 1D CNN 模型'''train_x, train_y = sliding_window(train, sw_width, in_start=0)n_timesteps, n_features, n_outputs = train_x.shape[1], train_x.shape[2], train_y.shape[1]model = Sequential()model.add(Conv1D(filters=16, kernel_size=3, activation='relu', input_shape=(n_timesteps, n_features)))model.add(MaxPooling1D(pool_size=2))model.add(Flatten())model.add(Dense(10, activation='relu'))model.add(Dense(units=n_outputs))model.compile(loss='mse', optimizer='adam', metrics=['accuracy'])print(model.summary())model.fit(train_x, train_y,epochs=epochs_num, batch_size=batch_size_set, verbose=verbose_set)return modeldef forecast(model, pred_seq, sw_width):'''该函数实现对输入数据的预测'''data = np.array(pred_seq)data = data.reshape((data.shape[0]*data.shape[1], data.shape[2]))input_x = data[-sw_width:, 0] # 获取输入数据的最后一周的数据input_x = input_x.reshape((1, len(input_x), 1)) # 重塑形状[1, sw_width, 1]yhat = model.predict(input_x, verbose=0) # 预测下周数据yhat = yhat[0] # 获取预测向量return yhatdef evaluate_model(model, train, test, sd_width):'''该函数实现模型评估'''history_fore = [x for x in train]predictions = list() # 用于保存每周的前向验证结果;for i in range(len(test)):yhat_sequence = forecast(model, history_fore, sd_width) # 预测下周的数据predictions.append(yhat_sequence) # 保存预测结果history_fore.append(test[i, :]) # 得到真实的观察结果并添加到历史中以预测下周predictions = np.array(predictions) # 评估一周中每天的预测结果score, scores = evaluate_forecasts(test[:, :, 0], predictions)return score, scoresdef model_plot(score, scores, days, name):'''该函数实现绘制RMSE曲线图'''plt.figure(figsize=(8,6), dpi=150)plt.plot(days, scores, marker='o', label=name)plt.grid(linestyle='--', alpha=0.5)plt.ylabel(r'$RMSE$', size=15)plt.title('CNN 模型预测结果',  size=18)plt.legend()plt.show()def main_run(dataset, sw_width, days, name, in_start, verbose, epochs, batch_size):'''主函数:数据处理、模型训练流程'''# 划分训练集和测试集train, test = split_dataset(dataset.values)# 训练模型model = cnn_model(train, sw_width, in_start, verbose_set=0, epochs_num=20, batch_size_set=4)# 计算RMSEscore, scores = evaluate_model(model, train, test, sw_width)# 打印分数summarize_scores(name, score, scores)# 绘图model_plot(score, scores, days, name)if __name__ == '__main__':dataset = pd.read_csv('household_power_consumption_days.csv', header=0, infer_datetime_format=True, engine='c',parse_dates=['datetime'], index_col=['datetime'])days = ['sun', 'mon', 'tue', 'wed', 'thr', 'fri', 'sat']name = 'cnn'sliding_window_width=7input_sequence_start=0epochs_num=20batch_size_set=4verbose_set=0main_run(dataset, sliding_window_width, days, name, input_sequence_start,verbose_set, epochs_num, batch_size_set)

输出:

Model: "sequential_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv1d_1 (Conv1D)            (None, 5, 16)             64        
_________________________________________________________________
max_pooling1d_1 (MaxPooling1 (None, 2, 16)             0         
_________________________________________________________________
flatten_1 (Flatten)          (None, 32)                0         
_________________________________________________________________
dense_2 (Dense)              (None, 10)                330       
_________________________________________________________________
dense_3 (Dense)              (None, 7)                 77        
=================================================================
Total params: 471
Trainable params: 471
Non-trainable params: 0
_________________________________________________________________
None
actual.shape[0]:46, actual.shape[1]:7
cnn: [400.800] 429.3, 391.4, 346.6, 395.3, 404.1, 320.0, 494.9

评估模型,打印一周预测的总体RMSE,以及一周中每天的每天RMSE。我们可以看到,在这种情况下,该模型比朴素模型性能更好,实现了400千瓦左右的整体RMSE,低于朴素模型实现的465千瓦。
在这里插入图片描述

每日RMSE图。该图显示,周二和周五的总有功功率比其他时间更容易预测,按标准周预测时,周六是最难预测的一天。

可以把滑动窗口宽度设置为14 重新实验:

sliding_window_width = 14
main_run(dataset, sliding_window_width, days, name, input_sequence_start,verbose_set, epochs_num, batch_size_set)

输出:

Model: "sequential_2"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv1d_2 (Conv1D)            (None, 12, 16)            64        
_________________________________________________________________
max_pooling1d_2 (MaxPooling1 (None, 6, 16)             0         
_________________________________________________________________
flatten_2 (Flatten)          (None, 96)                0         
_________________________________________________________________
dense_4 (Dense)              (None, 10)                970       
_________________________________________________________________
dense_5 (Dense)              (None, 7)                 77        
=================================================================
Total params: 1,111
Trainable params: 1,111
Non-trainable params: 0
_________________________________________________________________
None
actual.shape[0]:46, actual.shape[1]:7
cnn: [383.272] 390.3, 392.4, 348.3, 386.5, 374.8, 311.2, 462.5

在这里插入图片描述

可以看到RMSE降低了,关于超参数调整,以后的文章会介绍。


下篇文章介绍多通道CNN模型和多头(子模型异构)CNN模型。


参考:
https://machinelearningmastery.com/how-to-develop-convolutional-neural-networks-for-multi-step-time-series-forecasting/


这篇关于时间序列预测14:CNN 实现用电量/发电量预测的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/233788

相关文章

服务器集群同步时间手记

1.时间服务器配置(必须root用户) (1)检查ntp是否安装 [root@node1 桌面]# rpm -qa|grep ntpntp-4.2.6p5-10.el6.centos.x86_64fontpackages-filesystem-1.41-1.1.el6.noarchntpdate-4.2.6p5-10.el6.centos.x86_64 (2)修改ntp配置文件 [r

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount

业务中14个需要进行A/B测试的时刻[信息图]

在本指南中,我们将全面了解有关 A/B测试 的所有内容。 我们将介绍不同类型的A/B测试,如何有效地规划和启动测试,如何评估测试是否成功,您应该关注哪些指标,多年来我们发现的常见错误等等。 什么是A/B测试? A/B测试(有时称为“分割测试”)是一种实验类型,其中您创建两种或多种内容变体——如登录页面、电子邮件或广告——并将它们显示给不同的受众群体,以查看哪一种效果最好。 本质上,A/B测

Kubernetes PodSecurityPolicy:PSP能实现的5种主要安全策略

Kubernetes PodSecurityPolicy:PSP能实现的5种主要安全策略 1. 特权模式限制2. 宿主机资源隔离3. 用户和组管理4. 权限提升控制5. SELinux配置 💖The Begin💖点点关注,收藏不迷路💖 Kubernetes的PodSecurityPolicy(PSP)是一个关键的安全特性,它在Pod创建之前实施安全策略,确保P

uva 10131 最长子序列

题意: 给大象的体重和智商,求体重按从大到小,智商从高到低的最长子序列,并输出路径。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#include <stack>#include <vect