如何使用LSTM模型预测现有数据集范围外的未来时序数据
LSTM价格预测程序:未来外推预测实现及代码问题修正
问题背景
学习时序预测技术过程中编写了基于LSTM的基础价格预测程序,目前程序可正常运行,但仅能在已有数据集覆盖的时间范围内输出预测结果。期望实现突破现有数据集时间边界,预测尚未观测的未来数据(例如额外输出15个时间步的未来预测值),同时排查现有代码的逻辑缺陷。
当前使用的完整代码
from cProfile import label import pandas as pd import numpy as np import matplotlib.pyplot as plt from matplotlib.pylab import rcParams rcParams['figure.figsize'] = 20,10 from keras.models import Sequential from keras.layers import LSTM, Dropout, Dense from sklearn.preprocessing import MinMaxScaler df = pd.read_csv('./Data/market-price-3y.csv') # 仅保留日期和收盘价列 df = df[['Date', 'Close']] # 将日期列从object类型转为datetime类型 df['Date'] = pd.to_datetime(df.Date, format='%Y-%m-%d %H:%M:%S') # 将日期设为索引 df.index = df['Date'] print(df.head()) # 可视化历史收盘价 # plt.plot(df['Close'], label='Close Price History', color='red') # plt.show() df = df.sort_index(ascending=True, axis=0) data = pd.DataFrame(index=range(0, len(df)), columns=['Date', 'Close']) for i in range(0, len(data)): data['Date'][i] = df['Date'][i] data['Close'][i] = df['Close'][i] scaler = MinMaxScaler(feature_range=(0,1)) data.index = data.Date data.drop('Date', axis=1, inplace=True) # 拆分训练集、验证集 final_data = data.values train_data = final_data[0:900,:] valid_data = final_data[900:,:] scaler = MinMaxScaler(feature_range=(0,1)) scaled_data = scaler.fit_transform(final_data) x_train_data, y_train_data = [], [] for i in range(60, len(train_data)): x_train_data.append(scaled_data[i-60:i,0]) y_train_data.append(scaled_data[i,0]) x_train_data = np.asarray(x_train_data) y_train_data = np.asarray(y_train_data) x_train_data = np.reshape(x_train_data, (x_train_data.shape[0], x_train_data.shape[1],1)) # 构建LSTM模型 lstm_model = Sequential() lstm_model.add(LSTM(units=50, return_sequences=True, input_shape=(np.shape(x_train_data)[1], 1))) lstm_model.add(LSTM(units=50)) lstm_model.add(Dense(1)) model_data = data[len(data) - len(valid_data)-60:].values model_data = model_data.reshape(-1,1) model_data = scaler.transform(model_data) # 训练模型 lstm_model.compile(loss='mean_squared_error', optimizer='adam') lstm_model.fit(x_train_data, y_train_data, epochs=1, batch_size=1, verbose=2) # 构造测试集 X_test = [] for i in range(60, model_data.shape[0]): X_test.append(model_data[i-60:i,0]) X_test = np.array(X_test) X_test = np.reshape(X_test, (X_test.shape[0], X_test.shape[1], 1)) # 得到验证集预测结果 predicted_price = lstm_model.predict(X_test) predicted_price = scaler.inverse_transform(predicted_price) train_data = data[:900] valid_data = data[900:] valid_data['Predictions'] = predicted_price plt.plot(train_data['Close']) plt.plot(valid_data[['Close', 'Predictions']]) plt.show()
当前程序运行效果

当前预测结果在测试数据终点处停止,无法向外延伸预测未来时段数据。
现有代码存在的逻辑欠缺
- 数据泄露问题:代码中
MinMaxScaler在包含训练集、验证集的全量数据集上执行fit_transform,训练过程提前获取了验证集的数值分布信息,会导致验证集预测效果虚高,模型实际泛化能力差。 - 训练配置不合理:模型仅训练1个epoch,且
batch_size=1,没有充分学习时序数据规律,预测稳定性差;同时模型未加入Dropout正则化层,训练时容易出现过拟合。 - 冗余代码拉低运行效率:通过for循环逐行复制DataFrame列的操作完全多余,pandas原生的索引、复制接口1行代码即可实现相同功能,运行效率更高。
- 预测逻辑不支持时间外推:构造测试集时输入序列完全依赖已有的真实观测值,没有设计滚动迭代预测逻辑,因此只能输出已有数据覆盖时间范围内的结果,无法生成未来时段的预测值。
未来多步外推预测实现方法
当前搭建的是单步预测LSTM模型:输入长度为60的历史序列,输出1个时间步的预测值。不需要修改模型结构,新增滚动自回归预测逻辑即可实现数据集边界外的预测,以额外预测15个时间步为例,核心流程如下:
- 取数据集末尾最后60个真实值,作为第一轮预测的初始输入序列
- 将序列输入模型,得到第1个未来时间步的预测值
- 把刚生成的预测值追加到输入序列尾部,同时删除序列最开头的1个值,始终保持输入序列长度为60
- 重复步骤2、3,直到累计生成15个未来预测值
关键修改代码
首先修正数据泄露问题,调整归一化逻辑:
# 修正:scaler仅在训练集上拟合,避免数据泄露 scaler = MinMaxScaler(feature_range=(0,1)) scaled_train = scaler.fit_transform(train_data) # 全量数据、待预测数据仅用训练集得到的scaler做转换 scaled_full_data = scaler.transform(final_data) # 构造训练集逻辑保持不变,可适当调整训练参数 # 建议增加Dropout层、适当提高epoch数、调大batch_size提升训练效果 lstm_model = Sequential() lstm_model.add(LSTM(units=50, return_sequences=True, input_shape=(60, 1))) lstm_model.add(Dropout(0.2)) lstm_model.add(LSTM(units=50)) lstm_model.add(Dropout(0.2)) lstm_model.add(Dense(1)) lstm_model.compile(loss='mean_squared_error', optimizer='adam') lstm_model.fit(x_train_data, y_train_data, epochs=20, batch_size=32, verbose=2)
在原有验证集预测逻辑后,追加未来15步滚动预测代码:
# 保留原有验证集预测、逆归一化逻辑 # --- 新增未来预测部分 --- future_steps = 15 # 取全量数据最后60个值作为初始输入 last_seq = scaled_full_data[-60:].tolist() future_preds = [] for _ in range(future_steps): # 转换为模型要求的输入shape: (样本数, 时间步, 特征数) input_seq = np.array(last_seq[-60:]).reshape(1, 60, 1) # 预测下一个时间步 next_val = lstm_model.predict(input_seq, verbose=0)[0] future_preds.append(next_val) # 将预测值加入输入序列,供下一轮预测使用 last_seq.append(next_val.tolist()) # 逆归一化得到真实价格尺度的预测结果 future_preds = scaler.inverse_transform(future_preds)
最后修改可视化代码,把未来预测结果绘制到图上:
# 生成未来时间索引(按数据日度频率顺延,若数据采样频率不是天可自行修改freq参数) last_date = data.index[-1] future_dates = pd.date_range(start=last_date + pd.Timedelta(days=1), periods=future_steps, freq='D') future_df = pd.DataFrame(future_preds, index=future_dates, columns=['未来预测值']) # 绘图 plt.figure(figsize=(20,10)) plt.plot(train_data['Close'], label='训练集真实价格') plt.plot(valid_data['Close'], label='验证集真实价格') plt.plot(valid_data['Predictions'], label='验证集预测价格') plt.plot(future_df['未来预测值'], label='未来15步预测价格', linestyle='--', color='red') plt.legend() plt.show()
提示:这种单步滚动预测的方式,误差会随着预测步长增加不断累积,预测时长越久结果可靠性越低。如果需要做更长周期的预测,可以考虑直接构建多步输出的LSTM结构,或者采用Seq2Seq类的时序预测模型。
内容的提问来源于stack exchange,提问作者Timmy
相关产品推荐
相关产品推荐

