带有用于可变长度输入的屏蔽层的 Keras lstm
Keras lstm with masking layer for variable-length inputs
我知道这是一个有很多问题的主题,但我找不到解决问题的方法。
我正在使用掩蔽层在可变长度输入上训练 LSTM 网络,但它似乎没有任何效果。
输入形状 (100, 362, 24),其中 362 是最大序列长度,24 是特征数,100 是样本数(分为 75 个训练 / 25 个有效)。
输出形状 (100, 362, 1) 稍后转换为 (100, 362 - 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
层不传播遮罩。换句话说,Masking
层计算的mask张量被Lambda
层丢弃,因此Masking
层对输出损失没有影响。
如果您希望 Lambda
层的 compute_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_mask
returnsNone
而loss根本没有masked.
要解决此问题,由于您的 Lambda
图层本身没有引入任何额外的遮罩,compute_mask
方法应该只是 return 上一层的遮罩(适当切片以匹配图层的输出形状)。
masking_func = lambda inputs, previous_mask: previous_mask[:, N:]
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:, :], mask=masking_func))
现在您应该能够看到正确的损失值。
>> 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
).
我知道这是一个有很多问题的主题,但我找不到解决问题的方法。
我正在使用掩蔽层在可变长度输入上训练 LSTM 网络,但它似乎没有任何效果。
输入形状 (100, 362, 24),其中 362 是最大序列长度,24 是特征数,100 是样本数(分为 75 个训练 / 25 个有效)。
输出形状 (100, 362, 1) 稍后转换为 (100, 362 - 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
层不传播遮罩。换句话说,Masking
层计算的mask张量被Lambda
层丢弃,因此Masking
层对输出损失没有影响。
如果您希望 Lambda
层的 compute_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_mask
returnsNone
而loss根本没有masked.
要解决此问题,由于您的 Lambda
图层本身没有引入任何额外的遮罩,compute_mask
方法应该只是 return 上一层的遮罩(适当切片以匹配图层的输出形状)。
masking_func = lambda inputs, previous_mask: previous_mask[:, N:]
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:, :], mask=masking_func))
现在您应该能够看到正确的损失值。
>> 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
).