如何在TensorFlow v1中基于pandas DataFrame实现LSTM并解决相关报错
错误原因拆解
1. 初始维度报错根因
LSTM层的标准输入格式必须是**(样本数, 时间步长, 特征数)** 三维张量,你初始代码中DenseFeatures层输出的是二维张量(None, 2),其中None代表动态batch size,2是特征数量,缺少了时间步长维度,所以触发ndim不匹配的报错。
你之前加Reshape层报错是因为X_train.shape是(训练集总样本数, 特征数),你直接把整个训练集的样本总数当成了单样本的维度参数,单batch32个样本每个2特征总元素是32*2=64,你要reshape到包含总样本数的维度当然元素数不够,所以报64和8000不匹配。
2. 改用TimeseriesGenerator后的KeyError原因
你同时传了batch_input_shape和input_shape两个参数导致冲突,而且stateful模式的batch_input_shape参数顺序是**(batch_size, timesteps, features)**,你写反了时间步和batch的位置;其次TimeseriesGenerator输出的数据是numpy数组格式,没有DenseFeatures需要的字典键,你去掉了DenseFeatures层但没有调整数据输入逻辑,才会触发找不到键的报错。
3. 时间戳相关问题
不需要把时间戳单独设为一列,你用TimeseriesGenerator是按数据的顺序生成时间窗口,只要拆分数据集的时候没有打乱顺序(你代码里已经设了shuffle=False是对的),时序的先后关系已经保留了。有状态LSTM只要求你按时序顺序喂数据,且前后batch的样本是连续的时序片段即可。
适配TF1.15的可运行修复代码
使用TimeseriesGenerator后不需要DenseFeatures和字典格式的数据集,直接喂数组即可,调整后的代码:
import random import numpy as np import tensorflow as tf from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense from tensorflow.keras.preprocessing.sequence import TimeseriesGenerator from sklearn.model_selection import train_test_split # 全局参数设置 time_steps = 10 # 可根据你需要的历史窗口大小调整,这里代表用10个时间步的历史数据预测下一个值 batch_size = 32 np.random.seed(0) tf.random.set_random_seed(0) random.seed(0) # 假设frame为你的原始数据框 X = frame[['optimal_long_log_return', 'optimal_short_log_return']][:-1].values # 转numpy数组 Y = frame['equilibrium_log_return'].shift(-1)[:-1].values # 时序拆分不能打乱顺序 X_train, _X, y_train, _y = train_test_split(X, Y, test_size=0.5, shuffle=False, random_state=1) X_validation, X_test, y_validation, y_test = train_test_split(_X, _y, test_size=0.5, shuffle=False, random_state=1) # 生成时序窗口数据 train = TimeseriesGenerator(X_train, y_train, length=time_steps, batch_size=batch_size) validation = TimeseriesGenerator(X_validation, y_validation, length=time_steps, batch_size=batch_size) test = TimeseriesGenerator(X_test, y_test, length=time_steps, batch_size=batch_size) # 模型构建,stateful模式注意参数 model = Sequential(name='Expected Equilibrium Log Return') # stateful模式的batch_input_shape顺序是(batch_size, timesteps, features) # 注意要保证每个batch的样本数固定,最后不足一个batch的数据会被自动丢弃 model.add(LSTM(256, name='LSTM', stateful=True, batch_input_shape=(batch_size, time_steps, X_train.shape[1]))) model.add(Dense(1, name='Output')) model.compile(loss='logcosh', metrics=['mean_absolute_percentage_error'], optimizer='Adam') print(model.summary()) # 训练有状态LSTM需要每个epoch后手动重置状态 for epoch in range(10): model.fit_generator(train, epochs=1, validation_data=validation, verbose=1, shuffle=False) model.reset_states() loss, accuracy = model.evaluate_generator(test, verbose=0) print(f'Model Error: {accuracy}%')
关键调整说明
- 输入特征提前转成numpy数组,不需要用DenseFeatures和字典数据集,避免KeyError
- 显式指定时间步长参数
time_steps,控制LSTM每次输入用多少个历史时刻的特征 - 修正stateful模式下的
batch_input_shape参数顺序,删除冲突的input_shape参数 - 有状态LSTM训练时每个epoch结束后手动调用
reset_states()清理隐藏层状态,避免前后epoch的状态混淆 - 移除不必要的
sample_weight_mode='temporal'参数,单输出预测不需要时序加权
内容的提问来源于stack exchange,提问作者SnakeWasTheNameTheyGaveMe

