【问题标题】:How to extract last hidden state from bidirectional LSTM (encoder decoder structure)?如何从双向 LSTM(编码器解码器结构)中提取最后的隐藏状态?
【发布时间】:2022-12-04 02:27:04
【问题描述】:

对于我的单向作业,我给出了一个使用 PyTorch 的 seq2seq 模型的笔记本。方法中的一些代码已被删除,我必须将其填写。我们的讲师在代码中留下了一些待办事项以及以下对我们必须做的事情的解释:

“编码器的内部表示在第一个时间步中作为 dec_hid 参数传递。这是通过将编码器最后一个 LSTM 层的两个方向的两个最后状态相加并将其用作所有 LSTM 的初始状态来计算的解码器的层。为此,请使用解码器的前向方法中的扩展来计算 hid_h 或 hid_c。”

我试图实现它,但是当我训练模型时,我的验证损失停留在 37 左右。

class Decoder(nn.Module):
    def __init__(self, output_size, hidden_size, layers: int, dropout: float = 0.2):
        super().__init__()

        # TODO: initialise
        self.emb: nn.Module = nn.Embedding(num_embeddings, embedding_dim, padding_idx=0)
        self.lstm: nn.Module = nn.LSTM(input_size = embedding_dim, hidden_size = hidden_size, num_layers = layers, batch_first = True, dropout = dropout, bidirectional = False)
        self.project: nn.Module = nn.Linear(in_features = hidden_size, out_features = output_size) # initialize a linear layer that connects to outputs

        self.layers = layers
        self.hidden_size = hidden_size

    def rnn_forward(self, tgt_emb: tensor, hid: tensor = None) -> tuple:
        # TODO: calculate RNN output (dec_out) and hidden state (hid)
        tgt_emb_transformed = torch.sum(tgt_emb, -1)
        lengths: tensor = torch.tensor([torch.count_nonzero(x[1]) for x in enumerate(tgt_emb_transformed)])

        tgt_emb: tensor = pack_padded_sequence(tgt_emb, lengths, batch_first = True, enforce_sorted = False)

        dec_out, hid = self.lstm(tgt_emb)

        dec_out, lengths = pad_packed_sequence(dec_out, batch_first = True)

        return dec_out, hid

    def forward(self, enc_hid: tuple, tgt_seq: tensor, dec_hid=None) -> tuple:
        tgt_emb = self.emb(tgt_seq)

        if dec_hid is None:
            hid_h, hid_c = enc_hid
            # TODO: extract hidden state of last LSTM-layer from hid_h resp. hid_c 
            hid_h = hid_h[-1, :, :] + hid_h[-2, :, :]
            hid_c = hid_c[-1, :, :] + hid_c[-2, :, :]

            # TODO: expand this state to the number of decoder layers
            hid_h = hid_h.expand(self.layers, -1 ,self.hidden_size)
            hid_c = hid_h.expand(self.layers, -1 ,self.hidden_size)
            hid = (hid_h, hid_c)
        else:
            hid = dec_hid

        dec_out, hid = self.rnn_forward(tgt_emb, hid)

        # TODO: propagate through linear layer
        output: tensor = self.project(dec_out)

        return output, hid

我从两个方向提取了最后的状态,但我不确定如何处理它。在作业中它说添加它们然后使用扩展功能。我不明白一个与另一个有什么关系。我觉得这是唯一有意义的方法,但是当我们的老师告诉我们它应该下降到 2 左右时,我的损失再次停留在 37。有什么想法吗?

【问题讨论】:

    标签: python pytorch lstm seq2seq encoder-decoder


    【解决方案1】:

    在您提供的代码中,您似乎正试图通过将两个方向的最后两个状态相加来从双向 LSTM 编码器中获取最后一个隐藏状态。这不是在 PyTorch 中从双向 LSTM 中提取最后一个隐藏状态的正确方法。

    要从 PyTorch 中的双向 LSTM 中提取最后的隐藏状态,您首先需要从前向和反向 LSTM 中获取 hidden 状态。您可以通过使用适当的索引为双向 LSTM 的隐藏输出编制索引来完成此操作。例如,如果您的双向 LSTM 有两层,您将执行以下操作:

    # Get hidden states from forward LSTM
    hid_h_fwd = hidden[0][0]
    hid_c_fwd = hidden[1][0]
    
    # Get hidden states from backward LSTM
    hid_h_bwd = hidden[0][1]
    hid_c_bwd = hidden[1][1]
    

    一旦从两个方向获得隐藏状态,就可以沿着 hidden_size 维度将它们连接起来,以获得双向 LSTM 的最后一个隐藏状态。例如:

    # Concatenate hidden states from both directions
    last_hid_h = torch.cat([hid_h_fwd, hid_h_bwd], dim=1)
    last_hid_c = torch.cat([hid_c_fwd, hid_c_bwd], dim=1)
    

    作业中提到的 expand 函数用于扩展张量的维度。当您想对张量中的多个元素使用相同的值时,这很有用。例如,如果您想使用编码器的最后一个隐藏状态作为解码器中所有 LSTM 层的初始隐藏状态,您可以使用 expand 函数为解码器中的每个层复制最后一个隐藏状态。

    在您的代码中,您正在使用 expand 函数来扩展您通过将两个方向的两个最后状态相加而获得的最后一个隐藏状态的维度。但是,这不是从双向 LSTM 中提取最后一个隐藏状态的正确方法,因此该操作的结果将不正确。

    要修复您的代码,您应该将添加两个方向的两个最后状态的代码与我上面提供的代码一起替换,以从 PyTorch 中的双向 LSTM 中提取最后一个隐藏状态。这应该允许您的模型学习和提高其性能。

    我希望这有帮助!如果您有任何其他问题,请告诉我。

    【讨论】:

      猜你喜欢
      • 2018-10-30
      • 2018-09-19
      • 2018-11-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-09-11
      • 2018-09-21
      相关资源
      最近更新 更多