keras中的可变长度输出

use*_*181 5 deep-learning keras

我正在尝试在 keras 中创建一个自动编码器,其中输入和输出具有不同的时间步长。

model = Sequential()

#encoder
model.add(Embedding(vocab_size, embedding_size, mask_zero=True))
model.add(LSTM(units=hidden_size, return_sequences=False))

#decoder
model.add(RepeatVector(max_out_length))
model.add(LSTM(units=hidden_size, return_sequences=True))
model.add(TimeDistributed(Dense(num_class, activation='softmax')))
Run Code Online (Sandbox Code Playgroud)

对于输入来说,没有问题,因为只要整个批次具有相同的长度,网络就可以接受不同长度的输入。然而,问题在于输出大小是由 RepeatVector 长度决定的,并且没有简单的方法来改变它。

对于这样的问题有解决办法吗?

Dan*_*ler 4

如果您的意思是“可变长度的输入”和“与输入长度相同的输出”,您可以这样做:

警告:此解决方案必须使用批量大小 = 1
您将需要创建一个外部循环并将每个样本作为具有精确长度的 numpy 数组传递
您不能在此解决方案中使用掩码,并且正确的输出取决于输入

这是使用 Keras + Tensorflow 的工作代码:

进口:

from keras.layers import *
from keras.models import Model
import numpy as np
import keras.backend as K
from keras.utils.np_utils import to_categorical
Run Code Online (Sandbox Code Playgroud)

在 Lambda 层中使用的自定义函数:

#this function gets the length from the original input 
#and stores it in the final output of the encoder
def storeLength(x):
    inputTensor = x[0]
    storeInto = x[1] #the final output

    length = K.shape(inputTensor)[1]
    length = K.cast(length,K.floatx())
    length = K.reshape(length,(1,1))

    #will put length as the first element in the final output
    return K.concatenate([length,storeInto])


#this function expands the length of the input in the decoder
def expandLength(x):
    #lenght is the first element in the encoded input
    length = K.cast(x[0,0],'int32') #or int64 if necessary

    #the remaining elements are the actual data to be decoded
    data = x[:,1:]

    #a tensor with shape (length,)
    length = K.ones_like(K.arange(0,length))

    #make both length tensor and data tensor 3D and with paired dimensions 
    length = K.cast(K.reshape(length,(1,-1,1)),K.floatx())
    data = K.reshape(data,(1,1,-1))

    #this automatically repeats the elements based on the paired shapes
    return data*length 
Run Code Online (Sandbox Code Playgroud)

创建模型:

我假设输出等于输入,但由于您使用的是嵌入,所以我将“num_classes”设置为等于单词数。

对于这个解决方案,我们使用分支,因此我必须使用函数式 API Model。稍后会更好,因为您将需要使用 进行训练autoencoder.train_on_batch,然后使用 进行编码encoder.predict()或使用 进行解码decoder.predict()

vocab_size = 100
embedding_size = 7
num_class=vocab_size
hidden_size = 3

#encoder
inputs = Input(batch_shape = (1,None))
outputs = Embedding(vocab_size, embedding_size)(inputs)
outputs = LSTM(units=hidden_size, return_sequences=False)(outputs)
outputs = Lambda(storeLength)([inputs,outputs])
encoder = Model(inputs,outputs)

#decoder
inputs = Input(batch_shape=(1,hidden_size+1))
outputs = Lambda(expandLength)(inputs)
outputs = LSTM(units=hidden_size, return_sequences=True)(outputs)
outputs = TimeDistributed(Dense(num_class, activation='softmax'))(outputs)
decoder = Model(inputs,outputs)

#autoencoder
inputs = Input(batch_shape=(1,None))
outputs = encoder(inputs)
outputs = decoder(outputs)
autoencoder = Model(inputs,outputs)

#see each model's shapes 
encoder.summary()
decoder.summary()
autoencoder.summary()
Run Code Online (Sandbox Code Playgroud)

只是一个带有假数据的示例以及用于训练的方法:

inputData = []
outputData = []
for i in range(7,10):
    inp = np.arange(i).reshape((1,i))
    inputData.append(inp)

    outputData.append(to_categorical(inp,num_class))

autoencoder.compile(loss='mse',optimizer='adam')

for epoch in range(1):
    for inputSample,outputSample in zip(inputData,outputData):

        print(inputSample.shape,outputSample.shape)
        autoencoder.train_on_batch(inputSample,outputSample)

for inputSample in inputData:
    print(autoencoder.predict(inputSample).shape)
Run Code Online (Sandbox Code Playgroud)