获取 LSTM 中的预测状态

Getting state of predictions in LSTMs

我正在尝试使用以下模型生成莎士比亚文本:

model = Sequential()
model.add(Embedding(len_vocab, 64))
model.add(LSTM(256, return_sequences=True))
model.add(TimeDistributed(Dense(len_vocab, activation='softmax')))

model.compile(loss='sparse_categorical_crossentropy', optimizer='adam')
model.summary()

训练集由转换为数字的字符组成。其中 x 的形状为 (num_sentences, sentence_len) 并且 y 的形状相同,其中 y 只是 x 偏移一个字符。在这种情况下 sentence_len=40.

但是,当我预测时,我一次预测一个字符。请参阅下文,了解我如何使用模型进行拟合和预测:

for i in range(2):
    model.fit(x,y, batch_size=128, epochs=1)

    sentence = []
    letter = np.random.choice(len_vocab,1).reshape((1,1)) #choose a random letter
    for i in range(100):
        sentence.append(val2chr(letter))
        # Predict ONE letter at a time
        p = model.predict(letter)
        letter = np.random.choice(27,1,p=p[0][0])
    print(''.join(sentence))

但是,无论我训练了多少个 epoch,我得到的输出都是乱码。 一个可能的原因是我没有从之前的预测中得到cell memory

所以问题是如何确保在预测之前将状态发送到下一个单元格?

完整的 jupyter notebook 示例是 here:

编辑 1:

我刚刚意识到我需要发送之前的 LSTM 隐藏状态,而不仅仅是单元内存。 从那以后,我尝试将模型重做为:

batch_size = 64

model = Sequential()
model.add(Embedding(len_vocab, 64, batch_size=batch_size))
model.add(LSTM(256, return_sequences=True, stateful=True))
model.add(TimeDistributed(Dense(len_vocab, activation='softmax')))

model.compile(loss='sparse_categorical_crossentropy', optimizer='adam')
model.summary()

但是,现在我无法一次预测一个字母,因为它需要 batch_size 个输入。

使用Keras训练char-rnn的标准方法可以在官方示例中找到:lstm_text_generation.py

model = Sequential()
model.add(LSTM(128, input_shape=(maxlen, len(chars))))
model.add(Dense(len(chars)))
model.add(Activation('softmax'))

这个模型是基于 maxlen 个字符的序列训练的。 在训练该网络时,LSTM 状态会在每个序列后重置(默认情况下为 stateful=False)。

训练完这样的网络后,您可能希望一次输入和预测一个字符。最简单的方法(据我所知)是构建另一个具有相同结构的 Keras 模型,使用第一个模型的权重对其进行初始化,但在 Keras "stateful" 模式下使用 RNN 层:

model = Sequential()
model.add(LSTM(128, stateful=True, batch_input_shape=(1, 1, len(chars))))
model.add(Dense(len(chars)))
model.add(Activation('softmax'))

在此模式下,Keras 必须知道批次的完整形状(请参阅文档 here)。 由于您只想向网络提供一步字符的一个样本,因此批次的形状为 (1, 1, len(chars)).

正如@j-c-doe 所指出的,您可以将有状态选项与一批一起使用并转移权重。我发现的另一种方法是继续展开 LSTM 并进行如下预测:

for i in range(150):
    sentence.append(int2char[letter[-1]])
    p = model.predict(np.array(letter)[None,:])
    letter.append(np.random.choice(len(char2int),1,p=p[0][-1])[0])

注意:预测的维度非常重要! np.array(letter)[None,:] 给出 (1,i+1) 形状。这样就不需要修改模型。

最重要的是,它不断传递细胞状态记忆和隐藏状态。我不完全确定 stateful=True 它是否也通过了隐藏状态,或者它是否只是细胞状态。