【问题标题】:How to set an initial state for a Bidirectional LSTM Layer in Keras?如何在 Keras 中为双向 LSTM 层设置初始状态?
【发布时间】:2023-12-15 06:56:01
【问题描述】:

我正在尝试将由双向LSTM 层组成的编码器中的初始状态设置为 0。但是,如果我输入一个 0 的矩阵,我会收到一条错误消息,指出必须使用张量列表初始化双向层(这是有道理的)。当我尝试将这个 0 的矩阵复制到包含其中两个的列表中时(初始化两个 RNNs),我收到输入形状错误的错误。我在这里错过了什么?

class Encoder(tf.keras.Model):
def __init__(self, vocab_size, embedding_dim, enc_units, batch_sz):
    super(Encoder, self).__init__()
    self.batch_sz = batch_sz
    self.enc_units = enc_units
    self.embedding = keras.layers.Embedding(vocab_size, embedding_dim)
    self.lstmb = keras.layers.Bidirectional(lstm(self.enc_units, dropout=0.1))

def call(self, x, hidden):
    x = self.embedding(x)
    output, forward_h, forward_c, backward_h, backward_c = self.lstmb(x, initial_state=[hidden, hidden])
    return output, forward_h, forward_c, backward_h, backward_c


def initialize_hidden_state(batch_sz, enc_units):
    return tf.zeros((batch_sz, enc_units))

我得到的错误是:

ValueError: An `initial_state` was passed that is not compatible with `cell.state_size`. Received `state_spec`=[InputSpec(shape=(128, 512), ndim=2)]; however `cell.state_size` is [512, 512]

注意:函数initialize_hidden_state 的输出被馈送到调用函数的参数hidden

【问题讨论】:

    标签: python tensorflow machine-learning keras lstm


    【解决方案1】:

    阅读了所有的 cmets 和答案,我想我设法创建了一个工作示例。

    但首先要注意:

    • 我认为对self.lstmb 的调用只会返回所有五种状态,前提是您在 LSTM 的构造函数中指定它。
    • 我认为您不需要将隐藏状态作为隐藏状态列表传递。您应该将其作为初始状态传递。
    class Encoder(tf.keras.Model):
        def __init__(self, vocab_size, embedding_dim, enc_units, batch_sz):
            super(Encoder, self).__init__()
            self.batch_sz = batch_sz
            self.enc_units = enc_units
            self.embedding = tf.keras.layers.Embedding(vocab_size, embedding_dim)
            # tell LSTM you want to get the states, and sequences returned
            self.lstmb = tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(self.enc_units,
                                                                            return_sequences=True,
                                                                            return_state=True,
                                                                            dropout=0.1))
    
        def call(self, x, hidden):
            x = self.embedding(x)
            # no need to pass [hidden, hidden], just pass it as is
            output, forward_h, forward_c, backward_h, backward_c = self.lstmb(x, initial_state=hidden)
            return output, forward_h, forward_c, backward_h, backward_c
    
    
        def initialize_hidden_state(self):
            # I stole this idea from iamlcc, so the credit is not mine.
            return [tf.zeros((self.batch_sz, self.enc_units)) for i in range(4)]
    
    
    encoder = Encoder(vocab_inp_size, embedding_dim, units, BATCH_SIZE)
    
    # sample input
    sample_hidden = encoder.initialize_hidden_state()
    sample_output, forward_h, forward_c, backward_h, backward_c = encoder(example_input_batch, sample_hidden)
    print('Encoder output shape: (batch size, sequence length, units) {}'.format(sample_output.shape))
    print('Encoder forward_h shape: (batch size, units) {}'.format(forward_h.shape))
    print('Encoder forward_c shape: (batch size, units) {}'.format(forward_c.shape))
    print('Encoder backward_h shape: (batch size, units) {}'.format(backward_h.shape))
    print('Encoder backward_c shape: (batch size, units) {}'.format(backward_c.shape))
    

    【讨论】:

      【解决方案2】:

      您正在输入(batch_size, hidden_units) 的状态大小,您应该输入大小为(hidden_units, hidden_units) 的状态。它还必须有 4 个初始状态:2 个用于 2 个 lstm 状态,另外 2 个是因为由于双向,您有一个前向和一个后向传递。

      尝试改变这个:

      def initialize_hidden_state(batch_sz, enc_units):
          return tf.zeros((batch_sz, enc_units))
      

      def initialize_hidden_state(enc_units, enc_units):
          init_state = [np.zeros((enc_units, enc_units)) for i in range(4)]
          return init_state
      

      希望对你有帮助

      【讨论】:

      • 即使在此修复之后我仍然收到错误消息:ValueError:传递了与cell.state_size 不兼容的initial_state。收到state_spec=[InputSpec(shape=(512, 512), ndim=2)];但是 cell.state_size 是 [512, 512]
      • 我已经编辑了答案,尝试更改,看看它是否有效
      • 仍然不起作用,但我现在得到一个不同的错误:ValueError:将initial_state 传递给双向 RNN 时,状态应该是一个包含底层 RNN 状态的列表。找到:[]
      • Okey,然后恢复第一个错误并忘记编辑。不输入张量而是输入数组怎么样?在第一个错误中抱怨这个 ` state_spec=[InputSpec(shape=(512, 512), ndim=2)]; `
      • 好的,所以我提出以下建议。首先,不要输入初始状态。然后让网络返回它的状态,看看它有什么形状。像这样:keras.layers.Bidirectional(lstm(self.enc_units, dropout=0.1, return_state=True))
      【解决方案3】:

      如果还不算太晚,我认为你的initialize_hidden_state 函数应该是:

      def initialize_hidden_state(self): init_state = [tf.zeros((self.batch_sz, self.enc_units)) for i in range(4)] return init_state

      【讨论】:

      • 它仍然没有工作,我得到了与 OP 提到的完全相同的错误
      【解决方案4】:

      @BCJuan 有正确的答案,但我必须进行一些更改才能使其正常工作:

      def initialize_hidden_state(batch_sz, enc_units):
          init_state = [tf.zeros((batch_sz, enc_units)) for i in range(2)]
          return init_state
      

      非常重要:使用tf.zeros 而不是np.zeros,因为它需要一个 tf.tensor 类型。

      如果您在双向包装器中使用单个 LSTM 层,则需要返回一个包含 2 个 tf.tensorslist 来初始化每个 RNN。一个用于前传,一个用于后传。

      另外,如果您查看an example in TF's documentation,他们使用batch_szenc_units 来指定隐藏状态的大小。

      【讨论】:

      • 对于双向 LSTM,前向需要两个(单元状态和隐藏状态),后向需要两个。你总共需要 4 个。两个只用于 LSTM
      【解决方案5】:

      我最终没有使用双向包装器,而是创建了 2 个LSTM 层,其中一个接收参数go_backwards=True 并连接输出,如果它对任何人有帮助的话。 我认为双向 Keras 包装器目前无法处理此类事情。

      【讨论】:

        【解决方案6】:

        我用 tf.keras.Model 构建了我的编码器,但遇到了同样的错误。这个PR 可以帮助你。 最后我用 tf.keras.layers.layer 构建了我的模型,我还在努力。成功后我会更新!

        【讨论】: