使用DNN\LSTM\CNN进行时间序列预测

1. 项目背景

本节我们将在上节构建的数据窗口的基础上,尝试使用基本的深度神经网络算法进行测试,预测未来24h的家庭用电量。整个过程包括:
1.数据准备

1)查看数据,计算缺失值数量

2)估算缺失值

3)数值类型转换

4)构建DataTime对象

5)按小时数据重采样

6)去掉不完整的小时数
2.特征工程

1)识别季节性

2)时间编码

3)缩放数据
3.划分数据

按7:2:1划分数据集
4.为深度学习建模做准备

1)实现DataWindow类

2)定义compile_and_fit函数

3)创建列索引和列名字典
5.深度学习模型

  1. 训练基线模型
    2)训练DNN模型
    3)训练LSTM模型
    4)训练CNN模型
    5)训练LSTM+CNN模型
    6)训练自回归LSTM模型
    7)模型性能对比选择

本项目的源代码在闲鱼上,感兴趣可以看看:

闲鱼 https://m.tb.cn/h.7A4LlmB?tk=NmqjUpGmNYM CZ356\](https://m.tb.cn/h.7A4LlmB?tk=NmqjUpGmNYM CZ356 ) ## 2. 数据准备 ### 2.1 数据集介绍 该数据集是某家庭47个月的用电量,每分钟采集一次,总数据量超过200w点。数量集总共有9列,分别是:date(日期),time(时间),active_power(预测目标,有功功率),reactive_power(无功功率), voltage(电压), intensity(电流),metering_1(厨房区域有功功率),metering_2(洗衣房区域有功功率), metering_3(其他有功功率)。 ### 2.2 数据处理 1. 处理缺失值 通过此步骤处理缺失值和错误值,并将字符串变为数值类型,保证数据集的高质量和预测精度。 ```python # 保证每次运行的结果可以重现, 设置一个随机种子。 为什么是42?可以网上搜索有趣的故事 tf.random.set_seed(42) np.random.seed(42) df = pd.read_csv('../data/household_power_consumption.txt', sep=';') df.head() df.shape # 检查缺失值 df.isna().sum() ``` 返回结果展示sub_metering_3有25979个缺失值,需要进行处理。处理时有很多方法,常见的是缺失值填充和删除整列。 如果缺失值不集中,随机分布在数据中,则可以进行填充。如果缺失值集中,量大,而且没有明显的规律或算法来填充,则最好删除整个特征。 ```python #检查是否存在大量连续集中的缺失值 na_groups = df['Sub_metering_3'].notna().cumsum()[df['Sub_metering_3'].isna()] len_consecutive_na = na_groups.groupby(na_groups).agg(len) longest_na_gap = len_consecutive_na.max() longest_na_gap ``` 返回结果展示有7226分钟的连续缺失,无法进行填补,所有删除该行。 ```python df = df.drop(['Sub_metering_3'], axis=1) ``` 2. 类型转换 检查数据类型,如果是非数值类型,则需要转换后给到模型进行计算。 ```python df.dtypes #返回类型展示,所有数据都是object类型,需进行转换 #保留date,time列,后面再进行DataTime对象转换 cols_to_convert = df.columns[2:] df[cols_to_convert] = df[cols_to_convert].apply(pd.to_numeric, errors='coerce') df.dtypes #所有数据都是float64类型了 ``` 3. 数据重采样 数据集是分钟级别的,但我们预测目标是小时预测,所以需要按小时重采样。 这个过程包括构建datatime列,按小时重采样,去除不够1小时的数据列。 ```python # 创建datatime列 df.loc[:,'datetime'] = pd.to_datetime(df.Date.astype(str) + ' ' + df.Time.astype(str)) # 删除date和time列 df = df.drop(['Date', 'Time'], axis=1) df.head() # 按小时重采样 hourly_df = df.resample('H', on='datetime').sum() hourly_df.head() # 检查第一个小时和最后一个小时的数据。这两行都不满1小时,所以去掉。 # 刚刚中间去掉数据的部分,也应该进行类似处理,保证数据都是满1小时的数据。 hourly_df = hourly_df.drop(hourly_df.tail(1).index) hourly_df = hourly_df.drop(hourly_df.head(1).index) # 可以建立新索引,以整数为索引,将datetime变为普通的列 hourly_df = hourly_df.reset_index() ``` 绘制曲线查看,发现可能存在天级别的周期性规律,留在后面处理。 ![小时数据](https://i-blog.csdnimg.cn/direct/060770e05fb2462c8659bb1f1bab2e1c.png) 处理完成后,可以保存成干净的数据进行接下来的处理。 ## 3. 特征工程 ### 3.1 查看统计数据,删除无用列 第一步展示每列的基本统计信息,然后查看哪些特征大概率不会对预测结果产生影响。 ```python hourly_df.describe().transpose() ``` 结果如下: ![数据统计](https://i-blog.csdnimg.cn/direct/ac9612f53b9d4325a318d093c08eda6d.png) 从中看到,sub_metering_1数据在75%的时间内都是0,没有任何变化趋势,可能不会对结果产生影响,因此可以去掉。(如果数据量庞大,特征非常多,则推荐去掉) ```python hourly_df = hourly_df.drop(['Sub_metering_1'], axis=1) hourly_df.head() ``` ### 3.2 确定季节性周期 根据曲线很难确定周期性规律,需要通过傅里叶变换找到数据中真实的季节性规律。 ```python #对目标列进行傅里叶变换 fft = tf.signal.rfft(hourly_df['Global_active_power']) #获取频率数量 f_per_dataset = np.arange(0, len(fft)) #数据集有多少小时 n_sample_h = len(hourly_df['Global_active_power']) #数据集有多少周 hours_per_week = 24 * 7 weeks_per_dataset = n_sample_h / hours_per_week #数据集中一周的频率 f_per_week = f_per_dataset / weeks_per_dataset #绘制频率和振幅图 plt.step(f_per_week, np.abs(fft)) plt.xscale('log') # 标记 plt.xticks([1, 7], ['1/week', '1/day']) plt.xlabel('Frequency') plt.tight_layout() plt.show() plt.savefig('figures/CH18_F06_peixeiro.png', dpi=300) ``` ![周期性振幅](https://i-blog.csdnimg.cn/direct/5dbd0f05ab444c648b5091982a176864.png) 从图中可以看到,每天的季节性有个可见的峰值,但没有明显的每周的季节性。因此,我们判断用电量确实有每天的季节性。 ### 3.3 拆分和缩放数据 将数据拆分为训练集、测试集和验证集,并进行缩放。 ```python n = len(hourly_df) # 划分70:20:10 (train:validation:test) train_df = hourly_df[0:int(n*0.7)] val_df = hourly_df[int(n*0.7):int(n*0.9)] test_df = hourly_df[int(n*0.9):] #进行缩放处理 from sklearn.preprocessing import MinMaxScaler scaler = MinMaxScaler() # 为什么仅使用train数据进行缩放器训练? 如前几篇分析,是为了防止验证集和验证集的数据泄露。 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]) ``` ## 4. 深度学习介绍 深度学习是一种以神经网络为架构,通过多层非线性变换,对数据进行学习的算法。相对于传统基于数据原理及统计学的机器学习策略,深度学习能够使用海量的数据进行学习,从而达到更高的精度。 ### 4.1 深度学习常见概念 1. 神经网络结构:用于定义数据流动路径和计算方式。子概念包括全连接层、卷积层、池化层,输入/输出层,隐藏层等。 2. 前向传播:输入数据从输入层流向输出层,逐层计算得到预测值的过程。我们在使用各种AI模型推理的过程就是前向传播的过程。 3. 反向传播:根据损失函数误差,从输出层反向逐层计算每个参数的梯度的过程。 4. 优化器:根据反向传播计算出的梯度,更新网络权重及最小化损失函数的过程。常用的优化器包括SGD(随机梯度下降,容易不收敛), Momentum(动量优化器,加速并减少震荡),Adam(效果好,最常用) 5. 激活函数:每个神经元的计算因子,用于引入非线性,控制神经元是否被激活及输出信息。常见的激活函数包括:Sigmoid(容易梯度消失,常用于二分类),Tanh(收敛速度快于sigmoid),Relu(计算高效,最常用),Softmax(输出为概率分布,用于多分类) 6. 损失函数: 衡量模型预测结果与真实值之间差距的标量值。使用各种优化器的目的就是通过梯度下降,最小化这个损失函数的值。对于时间序列等回归任务,常见的有MSE(均方误差),MAE(平均绝对误差)。 7. 正则化,防止模型在训练时过拟合,提高泛化能力,常见方法包括L1/L2权重正则化,Dropout和早停等 8. 初始化:在训练开始前,对网络参数如权重w,偏置b等设置初始值 9. 学习率:优化器更新权重的步长大小,是最终的超参数之一。 10. Epoch: 整个训练集完整的通过神经网络计算并进行一次参数更新,叫做一轮。 11. Batch size: 单次前向/反向传播计算中使用的样本数量。 12. Iteration: 完成一轮计算所需要的批处理次数(总样本数/batch size) 13. 回调函数:它是一组在训练过程中的特点时间点(如一轮开始/结束时,每个批次处理后)被自动调用的函数,用于在训练过程中植入自己的代码,而不需修改训练循环本身。常见的回调函数有:模型检查点设置ModelCheckpoint、早停设置EarlyStopping,学习率调度器LearningRateScheduler,可视化监控TensorBoard。 ## 5. 建模前准备 在创建深度学习模型前,需要明确要导入的python库,并创建数据窗口。 ### 5.1 python库 ```python import numpy as np # 多维数据操作基础 import pandas as pd # 结构化数据处理,如dataframe,series数据结构 import tensorflow as tf import matplotlib.pyplot as plt # 绘图库 from tensorflow.keras import Model # keras的函数式API基类,用于创建复杂模型 from tensorflow.keras import Sequential # 顺序模型类,用于创建简单的层模型 from tensorflow.keras.optimizers import Adam # 自适应学习率优化器 from tensorflow.keras.callbacks import EarlyStopping # 早停回调函数 from tensorflow.keras.losses import MeanSquaredError # 均方差,回归任务常用损失函数 from tensorflow.keras.metrics import MeanAbsoluteError # 平均绝对值误差,回归任务评估指标 # Dense, 全连接层 # Conv1D, 一维卷积层,用于序列数据处理 # Reshape, 改变输入张量的形状而不改变数据 from tensorflow.keras.layers import Dense,Conv1D,LSTM,Lambda,Reshape,RNN,LSTMCell ``` ![各模块作用](https://i-blog.csdnimg.cn/direct/eca6f8c203794baea86d7eeeac42946d.png) ### 5.2 创建DataWindow类 无需重写,使用上篇文章中详细介绍的DataWindow类,不再做介绍。 ### 5.3定义训练函数 我们把model.complie,model.fit函数封装到一起,同时定义早停函数。 ```python # 接受一个模型,窗口函数。默认早停参数为3,即连续3个epoch周期内损失指标没有改变,则停止。 默认最大轮数为50 def compile_and_fit(model, window, patience=3, max_epochs=50): early_stopping = EarlyStopping(monitor='val_loss', patience=patience, mode='min') model.compile(loss=MeanSquaredError(), optimizer=Adam(), metrics=[MeanAbsoluteError()]) history = model.fit(window.train, epochs=max_epochs, validation_data=window.val, callbacks=[early_stopping]) return history ``` ### 5.4 用到的主要tf.keras模型介绍(tensorflow 2.16.1) \*\*1. EarlyStopping \*\* 早停函数,它的完整函数描述如下: tf.keras.callbacks.EarlyStopping( monitor='val_loss', # 被监测的量化值,默认为 val_loss min_delta=0, # 最小变化量,小于此值认为计算没有改善 patience=0, # 没有改善的轮数,达到这个数量没有改善则停止 verbose=0, # 控制是否输出进度记录。0不记录 mode='auto', baseline=None, # 监测量的极限值 restore_best_weights=False, #是否恢复最佳权重 start_from_epoch=0 ) **2. Dense** 定义全连接神经网络层。 tf.keras.layers.Dense( units, # 全连接层单元数,输出维度 activation=None, # 激活函数,默认线性激活函数 use_bias=True, # 是否添加偏置项b. out=activation(dot(input,kernel)+bias) kernel_initializer='glorot_uniform', #权重初始化器,决定权重矩阵(kernel,w)初始化的方法 bias_initializer='zeros', # 初始化偏置b的方法 kernel_regularizer=None, # 权重正则化器,用于对权重矩阵w施加正则化,防止过拟合 bias_regularizer=None, #偏置正则化器 activity_regularizer=None, #激活值正则化器, 用于对该层的输出(激活值)施加正则化 kernel_constraint=None, # 权重约束,在训练过程中对权重矩阵施加约束 bias_constraint=None, # 偏执约束 lora_rank=None, \*\*kwargs ) ![Dense全连接层参数](https://i-blog.csdnimg.cn/direct/3f35b465f59546aaabeafdbe816a36c2.png) **3. Squential** squential是一个线性堆叠层的容器,它接受要添加到模型中的层列表,可以指定模型名称 model=tf.keras.Sequential( layers=None, trainable=True, name=None ) **4.model.compile 模型配置** compile( optimizer='rmsprop', # 优化器,定义如何更新模型权重即如何最小化损失 loss=None, # 损失函数 loss_weights=None, metrics=None, # 评估指标 weighted_metrics=None, # 当样本有权重时,计算加权指标 run_eagerly=False, steps_per_execution=1, jit_compile='auto', auto_scale_loss=True # 在混合精度训练中自动缩放损失值以防止梯度下溢 ) **5.model.fit训练模型** model.fit( x=None, # 输入数据,当使用dateset时,不需要指定y参数。datawindow中的处理方法,返回dataset y=None, # 标签数据,即目标数据 batch_size=None, #批量大小,默认32 epochs=1, #训练轮数 verbose='auto', # 输出日志显示过程信息 callbacks=None, #回调函数列表 validation_split=0.0, # 验证集分割比例,从训练数据末尾切分一部分作为验证集 validation_data=None, # 验证集数据 shuffle=True, # 是否打乱数据 class_weight=None, sample_weight=None, initial_epoch=0, steps_per_epoch=None, # 每轮步数,默认为样本数/批次数 validation_steps=None, validation_batch_size=None, validation_freq=1 ) **6.LSTM** tf.keras.layers.LSTM( units, # LSTM的单元数量,输出维度 activation='tanh', #控制输出门的激活函数,影响最终输出 recurrent_activation='sigmoid',用于循环步骤,即输入们,遗忘门,输出门的激活函数 use_bias=True, kernel_initializer='glorot_uniform', #输入权重矩阵(W)初始化 recurrent_initializer='orthogonal', # 初始化循环权重举证(U)隐藏状态的权重矩阵 bias_initializer='zeros', unit_forget_bias=True, # 遗忘门的偏置初始化 kernel_regularizer=None # 输入权重正则化 recurrent_regularizer=None # 循环权重正则化 bias_regularizer=None # 偏置正则化 activity_regularizer=None # 输出正则化 kernel_constraint=None # 输入权重约束 recurrent_constraint=None # 循环权重约束 bias_constraint=None # 偏置约束 dropout=0.0, recurrent_dropout=0.0, seed=None, return_sequences=False, # 是否返回完整序列, 单层lstm用于分类,输出(batch_size,64) return_state=False, # 是否返回隐藏状态,true返回最后一个时间步的隐藏状态和细胞状态 go_backwards=False, stateful=False, # 是否保持批次间状态,false用于标准训练,批次独立 unroll=False, use_cudnn='auto', \*\*kwargs ) **7.Conv1D 一维卷积模型** tf.keras.layers.Conv1D( filters, # 输出空间的维度,卷积中滤波器的数量。数量越多捕捉的特征越丰富 kernel_size, # 卷积窗口大小。小窗口专注细节,大窗口感受整体 strides=1, #卷积步长,每次移动的步数 padding='valid', #输入数据的填充方式 data_format=None, dilation_rate=1, groups=1, activation=None, # 激活函数 use_bias=True, kernel_initializer='glorot_uniform', bias_initializer='zeros', kernel_regularizer=None, bias_regularizer=None, activity_regularizer=None, kernel_constraint=None, bias_constraint=None, \*\*kwargs ) ## 6. 基线模型 在每个项目开始,根据经验或感觉先定义一个基线模型,作为接下来选择的模型的比较基准。这个项目中,我们设置基线模型为重复最后24h的数据,因为我们根据周期性,判断有天的季节性,所以理所当然认为明天的能耗应该和今天的一样。 \*\* 1.定义数据窗口为输入长度24h,标签长度24h,位移24h,total_width = 48h\*\* ```python multi_window=DataWindow(input_width=24,shift=24,label_columns=['Global_active_power']) ``` \*\* 2. 定义基线模型,训练,保存结果并plot\*\* ```python # 定义一个继承自tf.keras.Model的基线模型类,接受label_index参数,使用父类的构造函数完成初始化 class RepeatBaseline(Model): def __init__(self, label_index=None): super().__init__() self.label_index = label_index def call(self, inputs): return inputs[:, :, self.label_index:] # 返回(batch_size,24,num_label_index)的数值 # 实例化模型,指定要预测的目标特征 baseline_repeat = RepeatBaseline(label_index=column_indices['Global_active_power']) # 编译模型,指定损失函数和评估指标 baseline_repeat.compile(loss=MeanSquaredError(), metrics=[MeanAbsoluteError()]) # 在验证集上评估模型 val_performance['Baseline - Repeat'] = baseline_repeat.evaluate(multi_window.val) # 在测试集上评估模型(不显示详细输出) performance['Baseline - Repeat'] = baseline_repeat.evaluate(multi_window.test, verbose=0) # 绘制模型的预测结果 multi_window.plot(baseline_repeat) ``` ![基线模型](https://i-blog.csdnimg.cn/direct/7ca0010fd8894facb38ed650fa4fdf74.png) ## 7. 线性模型 定义简单的线性模型,该模型只有输入层和输出层。因为只有一个预测目标,功率,所以输出层只有一个神经元。 ```python label_index = column_indices['Global_active_power'] num_features = train_df.shape[1] # 顺序模型堆叠, 只有一个输出单元,输入维度为global_active_power,global_reactive_power,intensity,voltage,sub_meter1 5个维度。这可以在Datawindow splite_to_inputs_labels函数中确定 linear = Sequential([ Dense(1, kernel_initializer=tf.initializers.zeros) ]) history = compile_and_fit(linear, multi_window) val_performance['Linear'] = linear.evaluate(multi_window.val) performance['Linear'] = linear.evaluate(multi_window.test, verbose=0) multi_window.plot(linear) ``` ![线性模型](https://i-blog.csdnimg.cn/direct/a369a85f73714ef0b19947b973936856.png) ## 8. DNN实现预测 深度神经网络是由输入层,隐藏层和输出层组成的全连接神经网络,是最基础的深度神经网络。在其他如CNN,RNN等模型中DNN常用于分类/回归,用作输出层,用于输出结果。 通过增加隐藏层,构建DNN模型。需要注意的是,DNN对每个时间步独立计算权重w和偏置b,不会考虑时间步之间的关系,无法理解和捕捉时间序列的关系。 ```python # 堆叠2层隐藏层,每个隐藏层64个神经元,并选择relu作为激活函数 dense = Sequential([ Dense(64, activation='relu'), Dense(64, activation='relu'), Dense(1, kernel_initializer=tf.initializers.zeros), ]) history = compile_and_fit(dense, multi_window) val_performance['Dense'] = dense.evaluate(multi_window.val) performance['Dense'] = dense.evaluate(multi_window.test, verbose=0) multi_window.plot(dense) ``` ![迭代过程输出](https://i-blog.csdnimg.cn/direct/d8251c1eab9841a99d30dd660cc2f1b6.png) *训练阶段756/756,没有指定steps_per_epoch参数。默认参数为总窗口数= 24209-48+1= 24162/32=755.06,向上取整为756。* \*评估阶段215/215,表示模型在验证集/测试集上的评估批次次数,这是训练完成后额外执行的一次完整评估,不属于训练epoch的一部分。验证集6917-48+1=6870/32=214.6,取整为215. ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/22294668375448009ec54b49311eaebf.png) ## 9. LSTM实现预测 时间序列预测的关键就在于变量是按照时间顺序排列的。处理这种序列数据的主流技术为RNN递归神经网络模型,它能够记录过去的信息并使用它处理序列的下一个元素。LSTM在RNN架构中增加了一个单元状态,避免了RNN的梯度消失问题,并且能够将过去的信息保留i在记忆中更长时间。 LSTM由三个门组成: 1)遗忘门,决定了来自过去步骤的那些信息仍然是相关的。 2)输入门,决定当前步骤的哪些信息是相关的。 3)输出门,决定将哪些信息传递到序列的下一个元素或作为结果传输到输出层。 实现代码如下: ```python lstm_model = Sequential([ LSTM(32, return_sequences=True), Dense(1, kernel_initializer=tf.initializers.zeros), ]) history = compile_and_fit(lstm_model, multi_window) val_performance['LSTM'] = lstm_model.evaluate(multi_window.val) performance['LSTM'] = lstm_model.evaluate(multi_window.test, verbose=0) multi_window.plot(lstm_model) ``` ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/8fd978102eaa4b288c5bba9090deab9e.png) ## 10. CNN实现预测 卷积神经网络可以通过卷积运算减少特征空间,在图像识别等领域应用广泛。卷积使用卷积核计算。卷积核的大小决定了它在卷积中每一步移动的步数。由于特征减少,训练更快,还支持并行计算。卷积操作减少了特征空间,因此必须提供稍长的输入序列确保输出序列包含24个时间步长,计算方法为: 输入长度=标签长度+核长度-1。对于本项目,定义核长度=3,输入长度=24+3-1=26。 代码如下: ```python #定义数据窗口 KERNEL_WIDTH = 3 LABEL_WIDTH = 24 INPUT_WIDTH = LABEL_WIDTH + KERNEL_WIDTH - 1 cnn_multi_window = DataWindow(input_width=INPUT_WIDTH, label_width=LABEL_WIDTH, shift=24, label_columns=['Global_active_power']) #定义1维卷积层作为输入层,将输出提供给具有32个神经元的全连接层,然后再进入输出层。 cnn_model = Sequential([ Conv1D(32, activation='relu', kernel_size=(KERNEL_WIDTH)), Dense(units=32, activation='relu'), Dense(1, kernel_initializer=tf.initializers.zeros), ]) history = compile_and_fit(cnn_model, cnn_multi_window) val_performance['CNN'] = cnn_model.evaluate(cnn_multi_window.val) performance['CNN'] = cnn_model.evaluate(cnn_multi_window.test, verbose=0) ``` ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/4e57adacf4fe476eb73d4ac6219182f5.png) ## 11. LSTM+CNN实现预测 可以将多种模型组合在一起进行预测,比如使用CNN进行特征过滤、特征提取,捕捉细节,然后再交给lstm进行处理。代码如下: ```python cnn_lstm_model = Sequential([ Conv1D(32, activation='relu', kernel_size=(KERNEL_WIDTH)), LSTM(32, return_sequences=True), Dense(1, kernel_initializer=tf.initializers.zeros), ]) history = compile_and_fit(cnn_lstm_model, cnn_multi_window) val_performance['CNN + LSTM'] = cnn_lstm_model.evaluate(cnn_multi_window.val) performance['CNN + LSTM'] = cnn_lstm_model.evaluate(cnn_multi_window.test, verbose=0) ``` ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/2a818ab26c4a41f9a6381a19a7191773.png) ## 12. 自回归LSTM模型实现预测 之前我们的预测模型都是一次性生成24h的数据,自回归lstm模型允许一次生成一个预测,并使用该预测作为下一个输入来生成下一个预测。这就为生成任意长度的预测带来了灵活性,不需要重新训练模型。但他会将误差引入后面的预测中,如果第一个预测非常差,这个错误会传递到后面的预测中,导致误差放大。代码如下: ```python #定义ARLSTM类 class AutoRegressive(Model): def __init__(self, units, out_steps): super().__init__() self.out_steps = out_steps # 表示预测序列的长度,项目中=24 self.units = units # 表示神经元的数量 self.lstm_cell = LSTMCell(units) # 允许访问更细粒度的信息,如状态核输出 self.lstm_rnn = RNN(self.lstm_cell, return_state=True) # rnn层包装lstmcell层,更容易在训练集上训练lstm self.dense = Dense(train_df.shape[1]) # 预测 # 定义输出第一个预测的函数,warmup复制一个单步lstm模型,将输入传递到lstm_rnn层,并从dense层获得预测,返回预测核状态 def warmup(self, inputs): x, *state = self.lstm_rnn(inputs) prediction = self.dense(x) return prediction, state # 重写model的call函数 def call(self, inputs, training=None): predictions = [] # 收集所有的预测结果 prediction, state = self.warmup(inputs) # 获取第一个预测结果 predictions.append(prediction) # 预测结果作为下一个预测的输入 for n in range(1, self.out_steps): x = prediction x, state = self.lstm_cell(x, states=state, training=training) # 使用上一个预测结果作为输入,生成新的预测 prediction = self.dense(x) predictions.append(prediction) #将预测结果堆叠起来 predictions = tf.stack(predictions) #将形状转换为(batch,time,features) predictions = tf.transpose(predictions, [1, 0, 2]) return predictions # 进行预测 AR_LSTM = AutoRegressive(units=32, out_steps=24) history = compile_and_fit(AR_LSTM, multi_window) val_performance['AR - LSTM'] = AR_LSTM.evaluate(multi_window.val) performance['AR - LSTM'] = AR_LSTM.evaluate(multi_window.test, verbose=0) ``` ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/c76186c9366748afb0444a41234d7978.png) ## 13. 比较结果,选择本项目效果最好的模型 ```python 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.005, 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.33) plt.xticks(ticks=x, labels=performance.keys()) plt.legend(loc='best') plt.tight_layout() ``` ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/2e011ea1ccf6497ea9c41d48b1b47339.png) 可以看到,本项目效果最好的模型是ARlstm自回归模型。 ## 14. 总结 通过1篇文章,将数据处理、常用的深度学习的时间序列模型作了介绍。模型没有好坏之分,并且模型可调参数非常多,遇到具体项目时也需要不断尝试和调试。如果遇到具体项目,可以一起沟通和尝试。

相关推荐
ppppppatrick9 小时前
【深度学习基础篇05】从AlexNet到ResNet:经典卷积神经网络的演进
人工智能·深度学习·cnn
Dev7z20 小时前
原创论文:基于LSTM神经网络的共享单车需求预测系统设计与实现
人工智能·神经网络·lstm
Katecat996631 天前
【计算机视觉】基于Faster R-CNN的线段检测与分割实现
计算机视觉·r语言·cnn
Dev7z2 天前
基于LSTM神经网络的共享单车需求预测系统设计与实现
人工智能·神经网络·lstm
ppppppatrick2 天前
【深度学习基础篇】手算卷积神经网络:13道经典题全解析(考研/面试必备)
深度学习·考研·cnn
Dev7z2 天前
原创论文:基于LSTM的共享单车需求预测研究
人工智能·rnn·lstm
All The Way North-2 天前
【LSTM系列·终篇】PyTorch nn.LSTM 终极指南:从API原理到双向多层实战,彻底告别维度错误!
pytorch·rnn·lstm·多层lstm·api详解·序列模型·双向lstm
Dev7z3 天前
基于LSTM的共享单车需求预测研究
人工智能·rnn·lstm
冰西瓜6003 天前
深度学习的数学原理(十二)—— CNN的反向传播
人工智能·深度学习·cnn