Keras LSTM连续输出模型及EarlyStopping使用问题咨询
LSTM回归模型问题解答
问题代码
# Import the Keras libraries and packages from keras.models import Sequential from keras.layers import Dense from keras.layers import LSTM from keras.layers import Dropout from keras.layers import TimeDistributed from keras import optimizers from keras.callbacks import EarlyStopping # x and y are numpy array with shape (1,251,1) x_test = x[:,-84:,:] # use the last 84 data as test data y_test = y[:,-84:,:] x_val = x[:,-36-84:-84,:] # the number of validation data is 36 y_val = y[:,-36-84:-84,:] x = x[:,:-36-84,:] # x and y are training data y = y[:,:-36-84,:] # the first 36 data are not used to calculate the loss, # set a numpy array for sample_weight used in Model.fit sw = (np.arange(251) >= 36).astype('float64') sw = np.reshape(sw, (1, sw.shape[0])) sw = sw[:,:-36-84] regressor = Sequential() # an LSTM model of 3 layers for k in range(3): if k == 0: regressor.add(LSTM(units = 200, return_sequences = True, input_shape = (None, 1))) else: regressor.add(LSTM(units = 200, return_sequences = True)) regressor.add(Dropout(0.2)) regressor.add(TimeDistributed(Dense(units = 1))) opt1 = optimizers.Adam(learning_rate=0.005,global_clipnorm=0.3) regressor.compile(optimizer = opt1, loss = 'mean_squared_error') callback = EarlyStopping(monitor='val_loss', patience=200, restore_best_weights=True) history = regressor.fit(x, y, epochs = 10000, batch_size = x.shape[0], sample_weight=sw, validation_data=(x_val, y_val), callbacks=[callback])
问题1:设置sample_weight忽略前36个时间步的损失计算是否正确?有没有其他方法?
你的sample_weight设置逻辑是正确的——通过将前36个时间步的权重设为0,确实能让这部分不参与损失计算。另外还有两种更简洁的实现方式:
- 直接截断训练数据:直接把训练集的前36个时间步砍掉,代码更直观:
这样训练时根本不会用到前36个时间步,无需额外设置sample_weight。x = x[:,36:,:] y = y[:,36:,:] - 自定义损失函数:在损失函数中直接跳过前36个时间步的计算:
编译模型时指定import tensorflow as tf def custom_mse(y_true, y_pred): # 仅计算第36步之后的均方误差 y_true = y_true[:, 36:, :] y_pred = y_pred[:, 36:, :] return tf.keras.losses.mean_squared_error(y_true, y_pred)loss=custom_mse即可。
问题2:因训练集和验证集时间步长不同,第一层LSTM设置input_shape=(None,1)是否正确?
完全正确。input_shape=(None,1)中的None表示模型可以接受任意长度的时间序列输入,正好适配训练集和验证集不同时间步长的场景,这也是处理可变长度序列输入的标准写法。
问题3:输出层是否符合每个时间步输出一个结果的要求?
符合要求。由于所有LSTM层都设置了return_sequences=True,每一层都会输出对应每个时间步的隐藏状态;最后接的TimeDistributed(Dense(1))会对每个时间步的隐藏状态单独执行全连接运算,最终输出的序列长度和输入序列一致,实现每个时间步输出一个回归结果。
问题4:计算val_loss时是否会包含Dropout层的作用?
不会。Keras在验证(或测试)阶段会自动关闭Dropout层的随机丢弃机制(同时切换BatchNormalization为推理模式),因此计算val_loss时使用的是完整的网络结构,这样能更准确反映模型的泛化能力,避免Dropout带来的随机干扰。
问题5:训练完成后验证集RMS值为0.00047,但模型最小val_loss为0.0010,两者不符,代码存在什么问题?
首先明确:RMS是MSE的平方根,0.00047的平方约为2.2e-7,和0.0010差距极大,说明你的计算逻辑存在错误,可能的原因包括:
- 数据混淆:计算RMS时用错了数据,比如误将训练集的预测结果当作验证集结果,或者搞混了
x_val和y_val的对应关系。检查RMS计算代码,确保用模型对x_val的预测值和真实标签y_val进行计算。 - 权重应用不一致:训练时你对训练集设置了sample_weight忽略前36个时间步,但计算验证集RMS时是否也跳过了对应时间步?如果验证集的损失计算范围和val_loss的计算范围不一致,会导致数值差异。
- 权重加载问题:确认
EarlyStopping的restore_best_weights=True是否生效。可以用regressor.evaluate(x_val, y_val)打印当前模型的验证损失,看是否和history.history['val_loss']中的最小值一致。如果不一致,可能是训练过程中权重被意外覆盖,或者回调函数未正确加载最优权重。
内容的提问来源于stack exchange,提问作者蘇泳誌
相关产品推荐
相关产品推荐

