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()
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()
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)
将原始数据进行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)
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()
通过观察这两条损失曲线,可以评估模型在训练集和验证集上的性能表现。通常来说,随着训练的进行,训练集损失应该逐渐降低,而验证集损失则应该在模型学到足够信息后趋于稳定。如果验证集损失开始上升,可能表示模型过拟合了训练数据
model.summary() # 打印模型摘要
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)
综合来看,这些指标表明你的模型在给定的任务上表现良好,能够对数据进行较为准确的预测
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()
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()
3. 总结
在本文中,我们探讨了LSTM模型的构建和训练过程,以及如何利用该模型进行时间序列数据的预测。强调了模型评估的重要性,通过均方误差、均方根误差和平均绝对误差等指标来量化模型的性能,不要忘记,在实际应用中,除了模型的构建,数据的质量和特征工程同样至关重要。通过不断学习和实践,我们可以不断优化模型并提高预测的准确性。感谢各位读者的耐心阅读,希望本文能够为你提供有益的信息。如果你对文章中的内容有任何疑问或想要进一步探讨的话题(当然博主能力也有限😢),欢迎联系。让我们一起继续学习,探索更多有趣的数据科学和机器学习领域的知识!感谢您的关注与支持,我们下期再见!
如果你对类似于这样的文章感兴趣。
欢迎关注、点赞、转发~
