基于LSTM模型的多输入多输出单步时间序列预测
2025-01-07
数据集为某天气预报数据,该数据集时间维度为2013年1月1日至2017年4月24日,存在 3 个特征分别是meantemp, humidity, meanpressure,接下来将以这三个特征为输入、输出,建立一个多输入多输出的LSTM模型。
1. 实现流程
代码实现基于LSTM多输入多输出模型的时间序列预测与分析,下面是代码的实现流程:
- 数据准备: 使用 pandas 库读取训练集和测试集的数据 对数据进行归一化处理,使用 MinMaxScaler 进行归一化,确保数据在相同的范围内。
- 数据预处理: 定义函数 prepare_data() 将数据转换为适合 LSTM 模型的格式也就是时间窗口划分,构建输入特征序列和对应的目标值序列。
- 构建模型: 使用 Keras 库构建多输入多输出的 LSTM 模型 定义输入层、LSTM 层、全连接层和输出层,其中输出层有三个分支分别预测 meantemp、humidity 和 meanpressure 编译模型,指定损失函数为均方误差(MSE)和优化器为 Adam。
- 模型训练: 使用 fit() 方法对模型进行训练,传入训练集和测试集,设置 epochs 和 batch_size。
- 模型评估: 使用模型对测试集进行预测,计算均方误差(MSE)、均方根误差(RMSE)、平均绝对误差(MAE)和拟合优度(R^2)等指标。
- 未来预测: 定义函数 predict_next_11_days() 预测未来 11 天的数据,利用模型的预测结果进行迭代预测。
- 可视化: 使用 Matplotlib 库绘制训练集、测试集、模型预测结果和未来预测结果的时序图。
整个流程涵盖了数据处理、模型构建、训练、评估和预测,以及结果的可视化和分析。
2. 代码实现
2.1 数据展示
import pandas as pd
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = 'SimHei' # 设置中文显示
plt.rcParams['axes.unicode_minus'] = False
df_train = pd.read_excel('训练集.xlsx', index_col=0, parse_dates=['date'])
df_test = pd.read_csv('测试集.csv', index_col=0, parse_dates=['date'])
df_train
其中df_train和df_test分别为模型的训练集和测试集,数据不存在缺失值、异常值。
2.2 时序图
2.2.1 训练集时序图
plt.figure(figsize=(15, 10))
plt.subplot(3, 1, 1)
plt.plot(df_train['meantemp'], color='y', alpha=0.3)
plt.title('meantemp时序图')
plt.grid(True)
plt.subplot(3, 1, 2)
plt.plot(df_train['humidity'], color='y', alpha=0.3)
plt.title('humidity时序图')
plt.grid(True)
plt.subplot(3, 1, 3)
plt.plot(df_train['meanpressure'], color='y', alpha=0.3)
plt.title('meanpressure时序图')
plt.grid(True)
plt.show()
2.2.2 测试集时序图
plt.figure(figsize=(15, 10))
plt.subplot(3, 1, 1)
plt.plot(df_test['meantemp'], color='g', alpha=0.3)
plt.title('meantemp时序图')
plt.grid(True)
plt.subplot(3, 1, 2)
plt.plot(df_test['humidity'], color='g', alpha=0.3)
plt.title('humidity时序图')
plt.grid(True)
plt.subplot(3, 1, 3)
plt.plot(df_test['meanpressure'], color='g', alpha=0.3)
plt.title('meanpressure时序图')
plt.grid(True)
plt.show()
2.3 数据0-1标准化
from sklearn.preprocessing import MinMaxScaler
def normalize_dataframe(train_df, test_df):
scaler = MinMaxScaler()
scaler.fit(train_df) # 在训练集上拟合归一化模型
train_data = pd.DataFrame(scaler.transform(train_df), columns=train_df.columns, index = df_train.index)
test_data = pd.DataFrame(scaler.transform(test_df), columns=test_df.columns, index = df_test.index)
return train_data, test_data
data_train, data_test = normalize_dataframe(df_train, df_test)
data_train
归一化时只使用训练集的统计量,并将归一化后的转换应用于训练集和测试集,避免直接对所有数据集进行归一化处理从而产生信息泄露。
2.4 滑动窗口划分数据
def prepare_data(data, win_size, target_feature_idxs):
num_features = data.shape[1]
X = []
y = []
for i in range(len(data) - win_size):
temp_x = data[i:i + win_size, :]
temp_y = [data[i + win_size, idx] for idx in target_feature_idxs]
X.append(temp_x)
y.append(temp_y)
X = np.asarray(X)
y = np.asarray(y)
return X, y
win_size = 12 # 时间窗口
target_feature_idxs = [0, 1, 2] # 指定待预测特征列索引
train_x, train_y = prepare_data(data_train.values, win_size, target_feature_idxs)
test_x, test_y = prepare_data(data_test.values, win_size, target_feature_idxs)
print("训练集形状:", train_x.shape, train_y.shape)
print("测试集形状:", test_x.shape, test_y.shape)
训练集形状 (1449, 12, 3) 表示:
1449:样本数,即训练集中有1449个样本。
12:时间窗口大小,每个样本有12个时间步长的数据,用于预测下一个时间步的数据。
3:特征数,每个时间步长有3个特征(meantemp、humidity 和 meanpressure)。
测试集形状 (102, 12, 3) 表示:
102:样本数,即测试集中有102个样本。
12:时间窗口大小,每个样本有12个时间步长的数据,用于预测下一个时间步的数据。
3:特征数,每个时间步长有3个特征(meantemp、humidity 和 meanpressure)。
2.5 模型建立
from keras.layers import LSTM, Dense
from keras.models import Model
from keras.layers import Input
# 输入维度
input_shape = Input(shape=(train_x.shape[1], train_x.shape[2]))
# LSTM层
lstm_layer = LSTM(128, activation='relu')(input_shape)
# 全连接层
dense_1 = Dense(64, activation='relu')(lstm_layer)
dense_2 = Dense(32, activation='relu')(dense_1)
# 输出层
output_1 = Dense(1, name='meantemp')(dense_2)
output_2 = Dense(1, name='humidity')(dense_2)
output_3 = Dense(1, name='meanpressure')(dense_2)
model = Model(inputs = input_shape, outputs = [output_1, output_2, output_3])
model.compile(loss='mse', optimizer='adam')
# 模型拟合
history = model.fit(train_x, [train_y[:,i] for i in range(train_y.shape[1])], epochs=100, batch_size=32, validation_data=(test_x, [test_y[:,i] for i in range(test_y.shape[1])]))
plt.figure()
plt.plot(history.history['loss'], c='b', label='loss')
plt.plot(history.history['val_loss'], c='g', label='val_loss')
plt.legend()
plt.show()
model.summary()
这是一个多输入多输出的 LSTM 模型,接受包含12个时间步长和3个特征的输入序列,在经过一层128个神经元的 LSTM 层和两个全连接层后,输出三个单独的预测结果,分别是 meantemp、humidity 和 meanpressure。
2.6 模型评价
from sklearn import metrics
y_pred = model.predict(test_x)
# 计算均方误差(MSE)
mse_meantemp = metrics.mean_squared_error(test_y[:,0], np.array([i for arr in y_pred[0] for i in arr]))
# 计算均方根误差(RMSE)
rmse_meantemp = np.sqrt(mse_meantemp)
# 计算平均绝对误差(MAE)
mae_meantemp = metrics.mean_absolute_error(test_y[:,0], np.array([i for arr in y_pred[0] for i in arr]))
from sklearn.metrics import r2_score # 拟合优度
r2_meantemp = r2_score(test_y[:,0], np.array([i for arr in y_pred[0] for i in arr]))
print("meantemp均方误差 (MSE):", mse_meantemp)
print("meantemp均方根误差 (RMSE):", rmse_meantemp)
print("meantemp平均绝对误差 (MAE):", mae_meantemp)
print("meantemp拟合优度:", r2_meantemp)
mse_humidity = metrics.mean_squared_error(test_y[:,1], np.array([i for arr in y_pred[1] for i in arr]))
rmse_humidity = np.sqrt(mse_humidity)
mae_humidity = metrics.mean_absolute_error(test_y[:,1], np.array([i for arr in y_pred[1] for i in arr]))
r2_humidity = r2_score(test_y[:,1], np.array([i for arr in y_pred[1] for i in arr]))
print("humidity均方误差 (MSE):", mse_humidity)
print("humidity均方根误差 (RMSE):", rmse_humidity)
print("humidity平均绝对误差 (MAE):", mae_humidity)
print("humidity拟合优度:", r2_humidity)
mse_meanpressure = metrics.mean_squared_error(test_y[:,2], np.array([i for arr in y_pred[2] for i in arr]))
rmse_meanpressure = np.sqrt(mse_meanpressure)
mae_meanpressure= metrics.mean_absolute_error(test_y[:,2], np.array([i for arr in y_pred[2] for i in arr]))
r2_meanpressure = r2_score(test_y[:,2], np.array([i for arr in y_pred[2] for i in arr]))
print("meanpressure均方误差 (MSE):", mse_meanpressure)
print("meanpressure均方根误差 (RMSE):", rmse_meanpressure)
print("meanpressure平均绝对误差 (MAE):", mae_meanpressure)
print("meanpressure拟合优度:", r2_meanpressure)
2.7 模型向后预测
def predict_next_11_days(model, input_data):
input_sequence = input_data.copy()
# 预测未来 11 天的数据
future_predictions = []
for _ in range(11):
predictions = model.predict(np.expand_dims(input_sequence[-1], axis=0))
next_data = np.append(input_sequence[-1, 1:], np.array(predictions).reshape(1,3), axis=0)
input_sequence = np.append(input_sequence, [next_data], axis=0)
future_predictions.append(predictions)
future_predictions = np.array(future_predictions).reshape(11, 3)
return future_predictions
future_predictions = predict_next_11_days(model, test_x[-1:])
future_predictions
2.8 预测可视化
# 反归一化
train_max_meantemp = np.max(df_train['meantemp'])
train_min_meantemp = np.min(df_train['meantemp'])
train_max_humidity = np.max(df_train['humidity'])
train_min_humidity = np.min(df_train['humidity'])
train_max_meanpressure = np.max(df_train['meanpressure'])
train_min_meanpressure = np.min(df_train['meanpressure'])
series_meantemp = np.array(future_predictions[:, 0])*(train_max_meantemp - train_min_meantemp)+train_min_meantemp
series_humidity = np.array(future_predictions[:, 1])*(train_max_humidity - train_min_humidity)+train_min_humidity
series_meanpressure = np.array(future_predictions[:, 2])*(train_max_meanpressure - train_min_meanpressure)+train_min_meanpressure
plt.figure(figsize=(20, 15), dpi =300)
plt.subplot(3,2,1)
plt.plot(pd.date_range(start='2013-01-13', end='2016-12-31', freq='D'), df_train.iloc[12::]['meantemp'],
label='训练集', color='blue', alpha=0.8)
plt.plot(pd.date_range(start='2017-01-01', end='2017-04-24', freq='D'), df_test['meantemp'],
label='测试集', color='gold', alpha=0.8)
plt.plot(pd.date_range(start='2017-01-13', end='2017-04-24', freq='D'),
y_pred[0]*(train_max_meantemp-train_min_meantemp)+train_min_meantemp,
label='测试集预测', color='navy', alpha=0.8)
plt.plot(pd.date_range(start='2017-04-24', end='2017-05-04', freq='D'),
series_meantemp, label='向后预测10天', color='limegreen', alpha=0.8)
plt.legend()
plt.title('meantemp')
plt.grid(True)
plt.xlabel('time')
plt.ylabel('°C')
plt.subplot(3,2, 2)
plt.plot(pd.date_range(start='2017-01-01', end='2017-04-24', freq='D'), df_test['meantemp'],
label='测试集', color='gold', alpha=0.8)
plt.plot(pd.date_range(start='2017-01-13', end='2017-04-24', freq='D'),
y_pred[0]*(train_max_meantemp-train_min_meantemp)+train_min_meantemp,
label='测试集预测', color='navy', alpha=0.8)
plt.plot(pd.date_range(start='2017-04-24', end='2017-05-04', freq='D'),
series_meantemp, label='向后预测10天', color='limegreen', alpha=0.8)
plt.legend()
plt.title('meantemp')
plt.grid(True)
plt.xlabel('time')
plt.ylabel('°C')
plt.subplot(3,2,3)
plt.plot(pd.date_range(start='2013-01-13', end='2016-12-31', freq='D'), df_train.iloc[12::]['humidity'],
label='训练集', color='blue', alpha=0.8)
plt.plot(pd.date_range(start='2017-01-01', end='2017-04-24', freq='D'), df_test['humidity'],
label='测试集', color='gold', alpha=0.8)
plt.plot(pd.date_range(start='2017-01-13', end='2017-04-24', freq='D'),
y_pred[1]*(train_max_humidity-train_min_humidity)+train_min_humidity,
label='测试集预测', color='navy', alpha=0.8)
plt.plot(pd.date_range(start='2017-04-24', end='2017-05-04', freq='D'),
series_humidity, label='向后预测10天', color='limegreen', alpha=0.8)
plt.legend()
plt.title('humidity')
plt.grid(True)
plt.xlabel('time')
plt.ylabel('°C')
plt.subplot(3,2,4)
plt.plot(pd.date_range(start='2017-01-01', end='2017-04-24', freq='D'), df_test['humidity'],
label='测试集', color='gold', alpha=0.8)
plt.plot(pd.date_range(start='2017-01-13', end='2017-04-24', freq='D'),
y_pred[1]*(train_max_humidity-train_min_humidity)+train_min_humidity,
label='测试集预测', color='navy', alpha=0.8)
plt.plot(pd.date_range(start='2017-04-24', end='2017-05-04', freq='D'),
series_humidity, label='向后预测10天', color='limegreen', alpha=0.8)
plt.legend()
plt.title('humidity')
plt.grid(True)
plt.xlabel('time')
plt.ylabel('°C')
plt.subplot(3,2,5)
plt.plot(pd.date_range(start='2013-01-13', end='2016-12-31', freq='D'), df_train.iloc[12::]['meanpressure'],
label='训练集', color='blue', alpha=0.8)
plt.plot(pd.date_range(start='2017-01-01', end='2017-04-24', freq='D'), df_test['meanpressure'],
label='测试集', color='gold', alpha=0.8)
plt.plot(pd.date_range(start='2017-01-13', end='2017-04-24', freq='D'),
y_pred[2]*(train_max_meanpressure-train_min_meanpressure)+train_min_meanpressure,
label='测试集预测', color='navy', alpha=0.8)
plt.plot(pd.date_range(start='2017-04-24', end='2017-05-04', freq='D'),
series_meanpressure, label='向后预测10天', color='limegreen', alpha=0.8)
plt.legend()
plt.title('meanpressure')
plt.grid(True)
plt.xlabel('pa')
plt.ylabel('°C')
plt.subplot(3,2,6)
plt.plot(pd.date_range(start='2017-01-01', end='2017-04-24', freq='D'), df_test['meanpressure'],
label='测试集', color='gold', alpha=0.8)
plt.plot(pd.date_range(start='2017-01-13', end='2017-04-24', freq='D'),
y_pred[2]*(train_max_meanpressure-train_min_meanpressure)+train_min_meanpressure,
label='测试集预测', color='navy', alpha=0.8)
plt.plot(pd.date_range(start='2017-04-24', end='2017-05-04', freq='D'),
series_meanpressure, label='向后预测10天', color='limegreen', alpha=0.8)
plt.legend()
plt.title('meanpressure')
plt.grid(True)
plt.xlabel('time')
plt.ylabel('pa')
plt.show()
同话题下的热门内容