Python 用于可变长度输入的带屏蔽层的Keras lstm

Python 用于可变长度输入的带屏蔽层的Keras lstm,python,keras,lstm,masking,Python,Keras,Lstm,Masking,我知道这是一个有很多问题的主题,但我找不到任何解决问题的方法 我正在使用屏蔽层在可变长度输入上训练LSTM网络,但似乎没有任何效果 输入形状(100362,24),其中362为最大序列长度,24为特征数,100为样本数(75列/25有效) 输出形状(100362,1)后来转换为(100362-N,1) 以下是我的网络的代码: from keras import Sequential from keras.layers import Embedding, Masking, LSTM, Lambda

我知道这是一个有很多问题的主题,但我找不到任何解决问题的方法

我正在使用屏蔽层在可变长度输入上训练LSTM网络,但似乎没有任何效果

输入形状(100362,24),其中362为最大序列长度,24为特征数,100为样本数(75列/25有效)

输出形状(100362,1)后来转换为(100362-N,1)

以下是我的网络的代码:

from keras import Sequential
from keras.layers import Embedding, Masking, LSTM, Lambda
import keras.backend as K


#                          O O O
#   example for N:3        | | |
#                    O O O O O O
#                    | | | | | | 
#                    O O O O O O

N = 5
y= y[:,N:,:]

x_train = x[:75]
x_test = x[75:]
y_train = y[:75]
y_test = y[75:]

model = Sequential()
model.add(Masking(mask_value=0., input_shape=(timesteps, features)))
model.add(LSTM(128, return_sequences=True))
model.add(LSTM(64, return_sequences=True))
model.add(LSTM(1, return_sequences=True))
model.add(Lambda(lambda x: x[:, N:, :]))

model.compile('adam', 'mae')

print(model.summary())
history = model.fit(x_train, y_train, 
                    epochs=3, 
                    batch_size=15, 
                    validation_data=[x_test, y_test])
我的数据在末尾被填充。例如:

>> x_test[10,350]
array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
   0., 0., 0., 0., 0., 0., 0.], dtype=float32)
问题是遮罩层似乎没有效果。我可以在培训期间打印的损失值中看到它,该值等于我在以下情况下计算的不带遮罩的值:

Layer (type)                 Output Shape              Param #   
=================================================================
masking_1 (Masking)          (None, 362, 24)           0         
_________________________________________________________________
lstm_1 (LSTM)                (None, 362, 128)          78336     
_________________________________________________________________
lstm_2 (LSTM)                (None, 362, 64)           49408     
_________________________________________________________________
lstm_3 (LSTM)                (None, 362, 1)            264       
_________________________________________________________________
lambda_1 (Lambda)            (None, 357, 1)            0         
=================================================================
Total params: 128,008
Trainable params: 128,008
Non-trainable params: 0
_________________________________________________________________
None
Train on 75 samples, validate on 25 samples
Epoch 1/3
75/75 [==============================] - 8s 113ms/step - loss: 0.1711 - val_loss: 0.1814
Epoch 2/3
75/75 [==============================] - 5s 64ms/step - loss: 0.1591 - val_loss: 0.1307
Epoch 3/3
75/75 [==============================] - 5s 63ms/step - loss: 0.1057 - val_loss: 0.1034

>> from sklearn.metrics import mean_absolute_error
>> out = model.predict(x_test, batch_size=1)
>> print('wo mask', mean_absolute_error(y_test.ravel(), out.ravel()))
>> print('w mask', mean_absolute_error(y_test[~(x_test[:,N:] == 0).all(axis=2)].ravel(), out[~(x_test[:,N:] == 0).all(axis=2)].ravel()))
wo mask 0.10343371
w mask 0.16236152
此外,如果我使用nan值作为屏蔽输出值,我可以看到nan在训练期间传播(损失等于nan)


要使遮罩层按预期工作,我缺少什么?

默认情况下,
Lambda
层不传播遮罩。换句话说,由
掩蔽
层计算的掩蔽张量被
Lambda
层丢弃,因此
掩蔽
层对输出损耗没有影响

如果希望
Lambda
层的
compute\u mask
方法传播上一个mask,则必须在创建层时提供
mask
参数。从
Lambda
层的源代码可以看出

def __init__(self, function, output_shape=None,
             mask=None, arguments=None, **kwargs):
    # ...
    if mask is not None:
        self.supports_masking = True
    self.mask = mask

# ...

def compute_mask(self, inputs, mask=None):
    if callable(self.mask):
        return self.mask(inputs, mask)
    return self.mask
由于
mask
的默认值为
None
compute\u mask
返回
None
,损失根本不被掩盖

要解决此问题,由于
Lambda
层本身不会引入任何额外的遮罩,因此
compute\u mask
方法应该只返回上一层的遮罩(使用适当的切片以匹配层的输出形状)

现在您应该能够看到正确的损失值

>> model.evaluate(x_test, y_test, verbose=0)
0.2660679519176483
>> out = model.predict(x_test)
>> print('wo mask', mean_absolute_error(y_test.ravel(), out.ravel()))
wo mask 0.26519736809498456
>> print('w mask', mean_absolute_error(y_test[~(x_test[:,N:] == 0).all(axis=2)].ravel(), out[~(x_test[:,N:] == 0).all(axis=2)].ravel()))
w mask 0.2660679670482195

使用NaN值填充不起作用,因为掩蔽是通过将损失张量乘以二进制掩蔽来完成的(
0*NaN
仍然是
NaN
,因此平均值将是
NaN
)。

掩蔽在KERA中有一些相当严重的问题。您的数据看起来在这里被填充了。由于LSTM正向运行,您可以尝试使用预填充进行上述操作吗?这不是根本原因,但“w掩码”行是错误的。您正在使用
y_test[(x_test[:,N:]==0)选择蒙面条目。所有(axis=2)]
。感谢您注意到输入错误Yu Yang,编辑此内容并测试您的答案:)这是否可能是CMAPSSData?很有魅力,非常感谢您提供完整的答案。从掩蔽层文档中可以看出,如果lambda层是guilty@Yu-杨:我想问一下,输入在屏蔽函数中起什么作用。同时,如果最后一个Lambda层没有进行切片操作呢?e、 例如,如果我使用Lambda(Lambda x:x+1),那么masking_func应该是masking_func=Lambda输入,previous_mask:previous_mask,对吗?@b在这种情况下,
inputs
变量无效。之所以需要它,是因为在
Lambda
层的
compute\u mask()
方法中,使用参数
self.mask(inputs,mask)
调用屏蔽函数。是的,如果层没有进行切片操作,那么您可以只返回
上一个\u掩码
,而不更改它。重塑层是否传播掩码?
>> model.evaluate(x_test, y_test, verbose=0)
0.2660679519176483
>> out = model.predict(x_test)
>> print('wo mask', mean_absolute_error(y_test.ravel(), out.ravel()))
wo mask 0.26519736809498456
>> print('w mask', mean_absolute_error(y_test[~(x_test[:,N:] == 0).all(axis=2)].ravel(), out[~(x_test[:,N:] == 0).all(axis=2)].ravel()))
w mask 0.2660679670482195