LSTM自动编码器

2024-04-29 01:49:51 发布

您现在位置:Python中文网/ 问答频道 /正文

我试图构建一个LSTM自动编码器,目的是从一个序列中获取一个固定大小的向量,它尽可能地表示序列。该自动编码器由两部分组成:

  • LSTM编码器:获取序列并返回输出向量(return_sequences = False
  • LSTM解码器:获取输出向量并返回序列(return_sequences = True

因此,最后,编码器是多对一LSTM,解码器是一对多LSTM。

enter image description here 图像源:Andrej Karpathy

在较高的级别上,代码如下所示(类似于here):

encoder = Model(...)
decoder = Model(...)

autoencoder = Model(encoder.inputs, decoder(encoder(encoder.inputs)))

autoencoder.compile(loss='binary_crossentropy',
              optimizer='adam',
              metrics=['accuracy'])

autoencoder.fit(data, data,
          batch_size=100,
          epochs=1500)

数组data的形状(训练示例数、序列长度、输入维度)是(1200, 10, 5),如下所示:

array([[[1, 0, 0, 0, 0],
        [0, 1, 0, 0, 0],
        [0, 0, 1, 0, 0],
        ..., 
        [0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0]],
        ... ]

问题:我不确定如何继续,尤其是如何将LSTM集成到Model以及如何让解码器从向量生成序列。

我正在使用kerastensorflow后端。

编辑:如果有人想试试,下面是我用移动序列(包括填充)生成随机序列的过程:

import random
import math

def getNotSoRandomList(x):
    rlen = 8
    rlist = [0 for x in range(rlen)]
    if x <= 7:
        rlist[x] = 1
    return rlist


sequence = [[getNotSoRandomList(x) for x in range(round(random.uniform(0, 10)))] for y in range(5000)]

### Padding afterwards

from keras.preprocessing import sequence as seq

data = seq.pad_sequences(
    sequences = sequence,
    padding='post',
    maxlen=None,
    truncating='post',
    value=0.
)

Tags: inimportencoderfordatamodelreturn序列
3条回答

模特可以随心所欲。如果我理解对了,你只想知道如何用LSTM创建模型?

使用LSTMs

首先,你必须定义你的编码向量是什么样子的。假设您希望它是一个由20个元素组成的数组,一个一维向量。所以,形状(无,20)。它的大小取决于你,没有明确的规则知道理想的。

你的输入必须是三维的,比如你的(1200,10,5)。在keras摘要和错误消息中,它将显示为(None,10,5),因为“None”表示批大小,每次训练/预测时,批大小都会有所不同。

有很多方法可以做到这一点,但是,假设您只需要一个LSTM层:

from keras.layers import *
from keras.models import Model

inpE = Input((10,5)) #here, you don't define the batch size   
outE = LSTM(units = 20, return_sequences=False, ...optional parameters...)(inpE)

这对于一个非常简单的编码器来说已经足够了,它可以生成一个包含20个元素的数组(但是如果需要,可以叠加更多的层)。让我们创建模型:

encoder = Model(inpE,outE)   

现在,对于解码器来说,它变得模糊了。你不再有一个真正的序列,而是一个静态的有意义的向量。你可能还想使用ltsm,他们会假设向量是一个序列。

但在这里,由于输入有shape(None,20),您必须首先将其重塑为某个三维数组,以便接下来附加一个LSTM层。

你将重塑它的方式完全取决于你。一个元素20步?一步20个元素?10步2个元素?谁知道呢?

inpD = Input((20,))   
outD = Reshape((10,2))(inpD) #supposing 10 steps of 2 elements    

需要注意的是,如果您不再有10个步骤,您将无法只启用“返回序列”并获得所需的输出。你得工作一点。实际上,不需要使用“返回序列”,甚至不需要使用LSTMs,但您可以这样做。

因为在我的整形中我有10个时间步(有意),所以可以使用“返回序列”,因为结果将有10个时间步(作为初始输入)

outD1 = LSTM(5,return_sequences=True,...optional parameters...)(outD)    
#5 cells because we want a (None,10,5) vector.   

您可以通过许多其他方式工作,例如只需创建一个50个单元格的LSTM而不返回序列,然后重新调整结果:

alternativeOut = LSTM(50,return_sequences=False,...)(outD)    
alternativeOut = Reshape((10,5))(alternativeOut)

我们的模式是:

decoder = Model(inpD,outD1)  
alternativeDecoder = Model(inpD,alternativeOut)   

在那之后,你把模型和你的代码结合起来,训练自动编码器。 这三个模型都有相同的权重,因此您可以使用编码器的predict方法使编码器产生结果。

encoderPredictions = encoder.predict(data)

关于生成序列的LSTMs,我经常看到的是类似于预测下一个元素的东西。

只取序列中的几个元素,然后尝试查找下一个元素。你又向前迈了一步,以此类推。这可能有助于生成序列。

您可以在这里找到一个简单的顺序到顺序自动编码器:https://blog.keras.io/building-autoencoders-in-keras.html

下面是一个例子

让我们创建一个由几个序列组成的合成数据。这个想法是通过自动编码器的镜头来观察这些序列。换言之,降低维度或将其汇总为固定长度。

# define input sequence
sequence = np.array([[0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9], 
                     [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8],
                     [0.2, 0.4, 0.6, 0.8],
                     [0.3, 0.6, 0.9, 1.2]])

# prepare to normalize
x = pd.DataFrame(sequence.tolist()).T.values
scaler = preprocessing.StandardScaler()
x_scaled = scaler.fit_transform(x)
sequence_normalized = [col[~np.isnan(col)] for col in  x_scaled.T]

# make sure to use dtype='float32' in padding otherwise with floating points
sequence = pad_sequences(sequence, padding='post', dtype='float32')

# reshape input into [samples, timesteps, features]
n_obs = len(sequence)
n_in = 9
sequence = sequence.reshape((n_obs, n_in, 1))

让我们设计一个简单的LSTM

#define encoder
visible = Input(shape=(n_in, 1))
encoder = LSTM(2, activation='relu')(visible)

# define reconstruct decoder
decoder1 = RepeatVector(n_in)(encoder)
decoder1 = LSTM(100, activation='relu', return_sequences=True)(decoder1)
decoder1 = TimeDistributed(Dense(1))(decoder1)

# tie it together
myModel = Model(inputs=visible, outputs=decoder1)

# summarize layers
print(myModel.summary())


#sequence = tmp
myModel.compile(optimizer='adam', loss='mse')

history = myModel.fit(sequence, sequence, 
                      epochs=400, 
                      verbose=0, 
                      validation_split=0.1, 
                      shuffle=True)

plot_model(myModel, show_shapes=True, to_file='reconstruct_lstm_autoencoder.png')
# demonstrate recreation
yhat = myModel.predict(sequence, verbose=0)
# yhat

import matplotlib.pyplot as plt

#plot our loss 
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.title('model train vs validation loss')
plt.ylabel('loss')
plt.xlabel('epoch')
plt.legend(['train', 'validation'], loc='upper right')
plt.show()

enter image description here

让我们构建自动编码器

# use our encoded layer to encode the training input
decoder_layer = myModel.layers[1]

encoded_input = Input(shape=(9, 1))
decoder = Model(encoded_input, decoder_layer(encoded_input))

# we are interested in seeing how the encoded sequences with lenght 2 (same as the dimension of the encoder looks like)
out = decoder.predict(sequence)

f = plt.figure()
myx = out[:,0]
myy = out[:,1]
s = plt.scatter(myx, myy)

for i, txt in enumerate(out[:,0]):
    plt.annotate(i+1, (myx[i], myy[i]))

下面是序列的表示

enter image description here

相关问题 更多 >