如何在LSTM自动编码器中使用掩蔽层来屏蔽输入/输出?

时间:2017-10-09 19:03:44

标签: machine-learning deep-learning keras lstm autoencoder

我正在尝试使用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的长度,对于不同的序列可能是不同的)。有谁知道是否有一些好的方法可以做到这一点?

谢谢!

2 个答案:

答案 0 :(得分:3)

选项1:如果您接受培训单独的批次,您可以随时进行无需填充的培训。

请参阅此答案,了解分离等长批次的简单方法:Keras misinterprets training data shape

在这种情况下,您所要做的就是执行"重复"以另一种方式操作,因为你在训练时没有确切的长度。

因此,您可以使用此代码来代替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])

Option2(闻起来并不好):在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])

答案 1 :(得分:1)

对于这个 LSTM Autoencoder 架构,我假设你已经理解了,由于 LSTM 编码器层具有 return_sequences=False,所以在 RepeatVector 中的 Mask 丢失了。

所以另一种选择,而不是像上面那样裁剪,也可以是传播掩码的create custom bottleneck layer