长短期记忆网络LSTM在时序数据预测演示

1. 长短期记忆网络LSTM简介

1.1 什么是LSTM?

LSTM是一种特殊的循环神经网络(RNN),专门设计用于解决传统RNN中的长期依赖问题。它在序列数据处理中表现出色,被广泛用于语言建模、时间序列预测和自然语言处理等领域

1.2 LSTM的内部结构:

LSTM的核心是其复杂的内部结构,包括三个关键门:遗忘门、输入门和输出门,这些门的作用是控制信息的流动,从而有效地捕捉序列中的长期依赖关

  • 遗忘门(Forget Gate):

其中, 是遗忘门的输出; 是权重矩阵; 是上一个时间步的隐藏状态; 是当前时间步的输入; 是 激活函数

  • 输入门(Input Gate):

其中, 是输入门的输出; 是当前时间步的细胞状态的候选值; 、 分别是权重矩阵; 是上一个时间步的隐藏状态; 是当前时间步的输入; 是 激活函数, 是双曲正切激活函数

  • 细胞状态更新:

其中 是当前时间步的细胞状态; 是遗忘门的输出; 是上一个时间步的细胞状态; 是输入门的输出; 是当前时间步的细胞状态候选值

  • 输出门(Output Gate):

其中 是输出门的输出; 是当前时间步的隐藏状态; 是权重矩阵; 是上一个时间步的隐藏状态; 是当前时间步的输入; 是当前时间步的细胞状态;

激活函数,

是双曲正切激活函数

这些公式描述了LSTM的内部运算过程,使其能够有效地处理长序列信息并减轻梯度消失问题

2. 代码实例

2.1 数据解读

在电力行业,对每日功率波动进行准确预测对于电网调度和能源规划至关重要。通过深度学习模型,可以更好地理解数据的复杂关系,从而提高预测的准确性,首先,加载数据并展示了原始功率波动数据(此数据已经过一定的异常值、缺失值处理)的趋势


          
import numpy as np
          
import matplotlib.pyplot as plt
          
import pandas as pd
          
plt.rcParams['font.sans-serif'] = 'SimHei' # 设置中文显示
          
plt.rcParams['axes.unicode_minus'] = False
          

          
data = pd.read_excel('每日功率波动.xlsx', index_col=0,  parse_dates=['数据时间'])
          
data.head()
      

picture.image


          
plt.figure(figsize=(15,5 ), dpi =300)
          
plt.grid(True)
          
plt.plot(data['每日功率波动'], label = '间隔15min数据',color = 'c')
          
plt.title('间隔15min功率')
          
plt.xlabel('时间')
          
plt.ylabel('每日功率波动')
          
plt.legend()
          
plt.show()
      

picture.image

2.2 数据处理

对原始数据进行数据的归一化处理,并将其转换为模型可接受的输入格式


          
# 0-1标准化
          
arr_max = np.max(np.array(data))
          
arr_min = np.min(np.array(data))
          
data_bz = (np.array(data)-arr_min)/(arr_max-arr_min)
          
data_bz = data_bz.ravel() # 转换为一维数组
          

          
def dataset(data, win_size=96): # 窗口为96正好为一天24h原始数据为间隔15min数据
          
    """
          
    将给定的数据划分为输入特征集合 X 和目标值集合 Y 的函数。
          

          
    参数:
          
    - data: 输入数据,应为一维数组或列表。
          
    - win_size: 窗口大小,表示每个样本的时间步数,默认为 96。
          

          
    返回值:
          
    - X: 输入特征集合,是形状为 (样本数, 窗口大小) 的二维数组。
          
    - Y: 目标值集合,是形状为 (样本数,) 的一维数组。
          
    """
          

          
    X = []  # 用于存储输入特征的列表
          
    Y = []  # 用于存储目标值的列表
          

          
    # 遍历数据,形成样本窗口
          
    for i in range(len(data) - win_size):
          
        temp_x = data[i:i + win_size]  # 提取窗口大小范围内的输入特征
          
        temp_y = data[i + win_size]    # 提取对应的目标值
          
        X.append(temp_x)
          
        Y.append(temp_y)
          

          
    # 转换列表为 NumPy 数组
          
    X = np.asarray(X)
          
    Y = np.asarray(Y)
          

          
    return X, Y
          
    
          
data_x, data_y = dataset(data_bz, 96)
          
print("输入特征集合:",data_x) # 二维数组
          
print("目标值集合:",data_y) # 一维数组
          

          
data_x = np.expand_dims(data_x, axis=1)
          
print(" (样本数, 1, 时间步数) :", data_x)
      

picture.image

picture.image

将原始数据进行0-1标准化,有助于模型更好地学习和收敛,将经过标准化处理的数据划分成输入特征集合(X)和目标值集合(Y)。这里通过窗口大小(win_size)将时间序列数据划分成多个样本,每个样本包含了过去一段时间的数据作为输入特征,以及对应时间步的数据作为目标值。这样的数据划分有助于训练模型学习时间序列的模式和趋势,最后

将 data_x 从一个形状为 (样本数, 时间步数) 的二维数组变为 (样本数, 1, 时间步数) 的三维数组。这种形状的调整通常在深度学习模型中使用,特别是对于循环神经网络 (RNN) 或卷积神经网络 (CNN) 等模型。三维数组中的第二个维度(1)通常用于表示时间步的维度,以便模型能够识别序列数据

2.3 模型构建


          
from sklearn.model_selection import train_test_split
          
from keras.layers import LSTM, Dense
          
from keras.models import Sequential
          

          
train_x, test_x, train_y, test_y = train_test_split(data_x, data_y, test_size = 0.2, shuffle = False)
          

          
# 使用 Sequential API 构建模型
          
model =Sequential()
          

          
#  LSTM 层中有 256 个隐藏单元 input_shape参数指定输入数据的形状,这里为(1, 时间步数)
          
model.add(LSTM(256, input_shape=(train_x.shape[1], train_x.shape[2])))
          

          
# 这里定义激活函数为sigmoid(前文对输出数据进行归一化处理)
          
model.add(Dense(1, activation='sigmoid')
          

          
# 编译模型
          
# 损失函数(loss)为均方误差(Mean Squared Error),适用于回归问题
          
# 优化器(optimizer)为 Adam,是一种常用的优化算法
          
model.compile(loss='mse', optimizer='adam')
          

          
# 训练模型
          
# epochs:训练的轮数,即遍历整个训练数据的次数
          
# batch_size:每批次训练的样本数
          
# validation_split:用于在训练过程中划分一部分数据作为验证集,这里是取训练数据的20%作为验证集
          
# shuffle:是否在每个 epoch 之前随机打乱训练数据
          
history = model.fit(train_x, train_y, epochs=18, batch_size=64, validation_split=0.2, shuffle=False)
      

picture.image


          
import matplotlib.pyplot as plt
          

          
# 绘制训练过程中的损失曲线和验证集损失曲线
          
plt.plot(history.history['loss'])          # 训练集损失曲线
          
plt.plot(history.history['val_loss'], c='r')  # 验证集损失曲线,使用红色表示
          
plt.legend(['loss', 'val_loss'])            
          
plt.show()
      

picture.image

通过观察这两条损失曲线,可以评估模型在训练集和验证集上的性能表现。通常来说,随着训练的进行,训练集损失应该逐渐降低,而验证集损失则应该在模型学到足够信息后趋于稳定。如果验证集损失开始上升,可能表示模型过拟合了训练数据


        
            

          model.summary() # 打印模型摘要
        
      

picture.image

2.4 模型评价


          
y_pred = model.predict(test_x)
          

          
from sklearn import metrics
          
# 计算均方误差(MSE)
          
mse = metrics.mean_squared_error(test_y, np.array([i for arr in y_pred for i in arr]))
          
# 计算均方根误差(RMSE)
          
rmse = np.sqrt(mse)
          
# 计算平均绝对误差(MAE)
          
mae = metrics.mean_absolute_error(test_y, np.array([i for arr in y_pred for i in arr]))
          
from sklearn.metrics import r2_score # 拟合优度
          
r2 = r2_score(test_y, np.array([i for arr in y_pred for i in arr]))
          
print("均方误差 (MSE):", mse)
          
print("均方根误差 (RMSE):", rmse)
          
print("平均绝对误差 (MAE):", mae)
          
print("拟合优度:", r2)
      

picture.image

综合来看,这些指标表明你的模型在给定的任务上表现良好,能够对数据进行较为准确的预测


          
plt.figure(figsize=(15,4), dpi =300)
          
plt.plot(test_y, color = 'c', label = '每日实际功率波动曲线')
          
plt.plot(y_pred, color = 'r', label = '预测每日功率波动曲线')
          
plt.title('每日实际功率波动与预测每日功率波动比对图')
          
plt.grid(True)
          
plt.xlabel('时间')
          
plt.ylabel('总有功功率(kw)')
          
plt.legend()
          
plt.show()
      

picture.image

2.5 模型向后预测


          
def predict_all(model, last_x, num=24):
          
    # 初始化一个列表,用于存储预测结果
          
    pred_y = []
          
    
          
    # 循环进行多步预测
          
    for i in range(num):
          
        # 使用模型进行单步预测
          
        temp_y = model.predict(last_x)
          
        
          
        # 将预测结果添加到预测列表中
          
        pred_y.append(temp_y[0, 0])
          
        
          
        # 为下一步预测准备输入数据
          
        temp_y = np.expand_dims(temp_y, 0)
          
        last_x = np.concatenate([last_x[:, :, 1:], temp_y], axis=2)
          
    
          
    # 将预测结果转换为 NumPy 数组并返回
          
    return np.asarray(pred_y)
          
    
          
# 从测试集中获取最后一个时间步的输入数据
          
last_x = test_x[-1]
          

          
# 使用 np.expand_dims 函数在最前面添加一个新的维度
          
last_x = np.expand_dims(last_x, 0)
          

          
# 往后预测10天 时间窗口为96一天每隔15min的数据采集
          
series_pre = predict_all(model, last_x, num=960) 
          

          
series = series_pre*(arr_max-arr_min)+arr_min # 未来10天波动预测值归一化数据还原
          

          
plt.figure(figsize=(10,3), dpi =500)
          
plt.plot(range(600), data[-600:], color = 'c', label = '原始数据')
          
plt.plot(range(600, 600+len(series)), series, color = 'r', label = '预测数据')
          
plt.title('未来10天波动预测')
          
plt.xlabel('时间')
          
plt.ylabel('总有功功率(kw)')
          
plt.grid(True)
          
plt.legend()
          
plt.savefig('未来10天波动预测.png')
          
plt.show()
      

picture.image

3. 总结

在本文中,我们探讨了LSTM模型的构建和训练过程,以及如何利用该模型进行时间序列数据的预测。强调了模型评估的重要性,通过均方误差、均方根误差和平均绝对误差等指标来量化模型的性能,不要忘记,在实际应用中,除了模型的构建,数据的质量和特征工程同样至关重要。通过不断学习和实践,我们可以不断优化模型并提高预测的准确性。感谢各位读者的耐心阅读,希望本文能够为你提供有益的信息。如果你对文章中的内容有任何疑问或想要进一步探讨的话题(当然博主能力也有限😢),欢迎联系。让我们一起继续学习,探索更多有趣的数据科学和机器学习领域的知识!感谢您的关注与支持,我们下期再见!

如果你对类似于这样的文章感兴趣。

欢迎关注、点赞、转发~

0
0
0
0
评论
未登录
暂无评论