我正在尝试使用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:如果你接受分批训练,你可以始终不进行填充训练。
请参阅这个答案,了解如何简单地分离等长度的批次:Keras误解训练数据形状
在这种情况下,你所需要做的就是以另一种方式执行“重复”操作,因为你在训练时没有确切的长度。
因此,你可以用以下代码代替RepeatVector
:
import keras.backend as Kdef repeatFunction(x): #x[0]是(batch,latent_dim) #x[1]是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#代替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]是已经重复的encoded #x[1]是inputs #padding = 1表示inputs中的实际数据,0表示0 padding = K.cast( K.not_equal(x[1][:,:,:1],0), dtype=K.floatx()) #假设你没有为非填充数据使用0 #padding在latent_dim上重复 padding = K.repeat_elements(padding,rep=latent_dim,axis=-1) return x[0]*paddinginputs = 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]是末尾的decoded #x[1]是inputs #两者具有相同的形状 #padding = 1表示inputs中的实际数据,0表示0 padding = K.cast( K.not_equal(x[1],0), dtype=K.floatx()) #如果你对非填充数据使用零,它们将失去反向传播 return x[0]*padding........decoded = LSTM(input_dim, return_sequences=True)(decoded)decoded = Lambda(cropOutputs,output_shape=(timesteps,input_dim))([decoded,inputs])