如何在 LSTM 自动编码器中使用掩蔽层来掩蔽 input/output?
How to use masking layer to mask input/output in LSTM autoencoders?
我正在尝试使用 LSTM 自动编码器以可变长度的序列作为输入进行序列到序列学习,使用以下代码:
inputs = Input(shape=(None, input_dim))
masked_input = Masking(mask_value=0.0, input_shape=(None,input_dim))(inputs)
encoded = LSTM(latent_dim)(masked_input)
decoded = RepeatVector(timesteps)(encoded)
decoded = LSTM(input_dim, return_sequences=True)(decoded)
sequence_autoencoder = Model(inputs, decoded)
encoder = Model(inputs, encoded)
其中 inputs
是用 0 填充到相同长度 (timesteps
) 的原始序列数据。使用上面的代码,输出的长度也是 timesteps
,但是当我们计算损失函数时,我们只需要输出的前 Ni
个元素(其中 Ni
是输入序列 i 的长度,对于不同的序列可能不同)。有谁知道有什么好的方法吗?
谢谢!
选项 1:如果您接受训练单独的批次,则始终可以不使用填充进行训练。
请参阅此关于分隔等长批次的简单方法的答案:
在这种情况下,您所要做的就是以另一种方式执行 "repeat" 操作,因为您在训练时没有确切的长度。
因此,您可以使用此代替 RepeatVector
:
import keras.backend as K
def repeatFunction(x):
#x[0] is (batch,latent_dim)
#x[1] is inputs: (batch,length,features)
latent = K.expand_dims(x[0],axis=1) #shape(batch,1,latent_dim)
inpShapeMaker = K.ones_like(x[1][:,:,:1]) #shape (batch,length,1)
return latent * inpShapeMaker
#instead of RepeatVector:
Lambda(repeatFunction,output_shape=(None,latent_dim))([encoded,inputs])
选项 2(味道不好):在 RepeatVector 之后使用另一个掩码。
我试过了,它起作用了,但我们最后没有得到 0,我们得到的最后一个值一直重复到最后。所以,你将不得不在你的目标数据中做一个奇怪的填充,重复最后一步直到结束。
示例:目标 [[[1,2],[5,7]]] 必须是 [[[1,2],[5,7],[5,7],[5, 7]...]]
我认为这可能会使您的数据失衡很多......
def makePadding(x):
#x[0] is encoded already repeated
#x[1] is inputs
#padding = 1 for actual data in inputs, 0 for 0
padding = K.cast( K.not_equal(x[1][:,:,:1],0), dtype=K.floatx())
#assuming you don't have 0 for non-padded data
#padding repeated for latent_dim
padding = K.repeat_elements(padding,rep=latent_dim,axis=-1)
return x[0]*padding
inputs = Input(shape=(timesteps, input_dim))
masked_input = Masking(mask_value=0.0)(inputs)
encoded = LSTM(latent_dim)(masked_input)
decoded = RepeatVector(timesteps)(encoded)
decoded = Lambda(makePadding,output_shape=(timesteps,latent_dim))([decoded,inputs])
decoded = Masking(mask_value=0.0)(decoded)
decoded = LSTM(input_dim, return_sequences=True)(decoded)
sequence_autoencoder = Model(inputs, decoded)
encoder = Model(inputs, encoded)
选项 3(最佳):直接从输入裁剪输出,这也消除了梯度
def cropOutputs(x):
#x[0] is decoded at the end
#x[1] is inputs
#both have the same shape
#padding = 1 for actual data in inputs, 0 for 0
padding = K.cast( K.not_equal(x[1],0), dtype=K.floatx())
#if you have zeros for non-padded data, they will lose their backpropagation
return x[0]*padding
....
....
decoded = LSTM(input_dim, return_sequences=True)(decoded)
decoded = Lambda(cropOutputs,output_shape=(timesteps,input_dim))([decoded,inputs])
对于这个 LSTM 自动编码器架构,我假设您已经理解,由于 LSTM 编码器层具有 return_sequences=False,掩码在 RepeatVector 处丢失。
所以另一种选择,而不是像上面那样裁剪,也可以是传播掩码的。
我正在尝试使用 LSTM 自动编码器以可变长度的序列作为输入进行序列到序列学习,使用以下代码:
inputs = Input(shape=(None, input_dim))
masked_input = Masking(mask_value=0.0, input_shape=(None,input_dim))(inputs)
encoded = LSTM(latent_dim)(masked_input)
decoded = RepeatVector(timesteps)(encoded)
decoded = LSTM(input_dim, return_sequences=True)(decoded)
sequence_autoencoder = Model(inputs, decoded)
encoder = Model(inputs, encoded)
其中 inputs
是用 0 填充到相同长度 (timesteps
) 的原始序列数据。使用上面的代码,输出的长度也是 timesteps
,但是当我们计算损失函数时,我们只需要输出的前 Ni
个元素(其中 Ni
是输入序列 i 的长度,对于不同的序列可能不同)。有谁知道有什么好的方法吗?
谢谢!
选项 1:如果您接受训练单独的批次,则始终可以不使用填充进行训练。
请参阅此关于分隔等长批次的简单方法的答案:
在这种情况下,您所要做的就是以另一种方式执行 "repeat" 操作,因为您在训练时没有确切的长度。
因此,您可以使用此代替 RepeatVector
:
import keras.backend as K
def repeatFunction(x):
#x[0] is (batch,latent_dim)
#x[1] is inputs: (batch,length,features)
latent = K.expand_dims(x[0],axis=1) #shape(batch,1,latent_dim)
inpShapeMaker = K.ones_like(x[1][:,:,:1]) #shape (batch,length,1)
return latent * inpShapeMaker
#instead of RepeatVector:
Lambda(repeatFunction,output_shape=(None,latent_dim))([encoded,inputs])
选项 2(味道不好):在 RepeatVector 之后使用另一个掩码。
我试过了,它起作用了,但我们最后没有得到 0,我们得到的最后一个值一直重复到最后。所以,你将不得不在你的目标数据中做一个奇怪的填充,重复最后一步直到结束。
示例:目标 [[[1,2],[5,7]]] 必须是 [[[1,2],[5,7],[5,7],[5, 7]...]]
我认为这可能会使您的数据失衡很多......
def makePadding(x):
#x[0] is encoded already repeated
#x[1] is inputs
#padding = 1 for actual data in inputs, 0 for 0
padding = K.cast( K.not_equal(x[1][:,:,:1],0), dtype=K.floatx())
#assuming you don't have 0 for non-padded data
#padding repeated for latent_dim
padding = K.repeat_elements(padding,rep=latent_dim,axis=-1)
return x[0]*padding
inputs = Input(shape=(timesteps, input_dim))
masked_input = Masking(mask_value=0.0)(inputs)
encoded = LSTM(latent_dim)(masked_input)
decoded = RepeatVector(timesteps)(encoded)
decoded = Lambda(makePadding,output_shape=(timesteps,latent_dim))([decoded,inputs])
decoded = Masking(mask_value=0.0)(decoded)
decoded = LSTM(input_dim, return_sequences=True)(decoded)
sequence_autoencoder = Model(inputs, decoded)
encoder = Model(inputs, encoded)
选项 3(最佳):直接从输入裁剪输出,这也消除了梯度
def cropOutputs(x):
#x[0] is decoded at the end
#x[1] is inputs
#both have the same shape
#padding = 1 for actual data in inputs, 0 for 0
padding = K.cast( K.not_equal(x[1],0), dtype=K.floatx())
#if you have zeros for non-padded data, they will lose their backpropagation
return x[0]*padding
....
....
decoded = LSTM(input_dim, return_sequences=True)(decoded)
decoded = Lambda(cropOutputs,output_shape=(timesteps,input_dim))([decoded,inputs])
对于这个 LSTM 自动编码器架构,我假设您已经理解,由于 LSTM 编码器层具有 return_sequences=False,掩码在 RepeatVector 处丢失。
所以另一种选择,而不是像上面那样裁剪,也可以是传播掩码的