在 PyTorch 中使用 PackedSequence 时如何处理 LSTM 层之间的丢失?

How to deal with dropout in between LSTM layers when using PackedSequence in PyTorch?

我正在为我的硕士论文创建一个用于特征提取的 LSTM 自动编码器。但是,我在将 dropout 与 LSTM 层结合起来时遇到了很多麻烦。

因为它是一个自动编码器,我遇到了一个瓶颈,这是通过有两个单独的 LSTM 层来实现的,每个层都有 num_layers=1,并且在它们之间有一个 dropout。我有不同长度的时间序列,因此发现打包序列是个好主意。

但是,根据我的实验,我必须在第一个 LSTM 之前打包数据,在 dropout 之前解包,然后在第二个 LSTM 之前再次打包。这似乎非常低效。有没有更好的办法?我在下面提供了一些示例代码和实现它的替代方法。

当前有效但可能不是最优的解决方案:

class Encoder(nn.Module):

    def __init__(self, seq_len, n_features, embedding_dim, hidden_dim, dropout):
        super(Encoder, self).__init__()

        self.seq_len = seq_len
        self.n_features = n_features
        self.embedding_dim = embedding_dim
        self.hidden_dim = hidden_dim

        self.lstm1 = nn.LSTM(
            input_size=n_features,
            hidden_size=self.hidden_dim,
            num_layers=1,
            batch_first=True,
        )

        self.lstm2 = nn.LSTM(
            input_size=self.hidden_dim,
            hidden_size=embedding_dim,
            num_layers=1,
            batch_first=True,
        )

        self.drop1 = nn.Dropout(p=dropout, inplace=False)

    def forward(self, x):
        x, (_, _) = self.lstm1(x)
        x, lens = pad_packed_sequence(x, batch_first=True, total_length=self.seq_len)
        x = self.drop1(x)
        x = pack_padded_sequence(x, lens, batch_first=True, enforce_sorted=False)
        x, (hidden_n, _) = self.lstm2(x)

        return hidden_n.reshape((-1, self.n_features, self.embedding_dim)), lens

备选方案,可能更好,但目前无效;

class Encoder2(nn.Module):

    def __init__(self, seq_len, n_features, embedding_dim, hidden_dim, dropout):
        super(Encoder2, self).__init__()

        self.seq_len = seq_len
        self.n_features = n_features
        self.embedding_dim = embedding_dim
        self.hidden_dim = hidden_dim

        self.lstm1 = nn.LSTM(
            input_size=n_features,
            hidden_size=self.hidden_dim,
            num_layers=2,
            batch_first=True,
            dropout=dropout,
            proj_size=self.embedding_dim,
        )

    def forward(self, x):
        _, (h_n, _) = self.lstm1(x)
        return h_n[-1].unsqueeze(1), lens

任何有关处理时间序列、打包序列、lstm-cells 和 dropout 的帮助和提示都将不胜感激,因为我在互联网上的其他地方找不到太多 documentation/guidance。谢谢!

最好的,拉尔斯·安基尔

为了以后的工作,经过大量的反复试验,以下自动编码器的完整代码似乎工作得很好。让打包和拆包正常工作是主要障碍。我认为,线索是尝试通过使用 proj_sizenum_layersdropout 参数来发挥 LSTM 模块的价值。

class EncoderV4(nn.Module):
    def __init__(
        self, seq_len, n_features, embedding_dim, hidden_dim, dropout, num_layers
    ):
        super().__init__()

        self.seq_len = seq_len
        self.n_features = n_features
        self.embedding_dim = embedding_dim
        self.hidden_dim = hidden_dim
        self.num_layers = num_layers

        self.lstm1 = nn.LSTM(
            input_size=n_features,
            hidden_size=self.hidden_dim,
            num_layers=num_layers,
            batch_first=True,
            dropout=dropout,
            proj_size=self.embedding_dim,
        )

    def forward(self, x):
        _, (h_n, _) = self.lstm1(x)
        return h_n[-1].unsqueeze(1)


class DecoderV4(nn.Module):
    def __init__(self, seq_len, input_dim, hidden_dim, n_features, num_layers):
        super().__init__()

        self.seq_len = seq_len
        self.input_dim = input_dim
        self.hidden_dim = hidden_dim
        self.n_features = n_features
        self.num_layers = num_layers

        self.lstm1 = nn.LSTM(
            input_size=input_dim,
            hidden_size=hidden_dim,
            num_layers=num_layers,
            proj_size=n_features,
            batch_first=True,
        )

    def forward(self, x, lens):
        x = x.repeat(1, self.seq_len, 1)
        x = pack_padded_sequence(x, lens, batch_first=True, enforce_sorted=False)
        x, _ = self.lstm1(x)

        return x


class RecurrentAutoencoderV4(nn.Module):
    def __init__(
        self, seq_len, n_features, embedding_dim, hidden_dim, dropout, num_layers
    ):
        super().__init__()

        self.encoder = EncoderV4(
            seq_len, n_features, embedding_dim, hidden_dim, dropout, num_layers
        )
        self.decoder = DecoderV4(
            seq_len, embedding_dim, hidden_dim, n_features, num_layers
        )

    def forward(self, x, lens):

        x = self.encoder(x)
        x = self.decoder(x, lens)

        return x

完整代码和使用此自动编码器的论文可分别在 GitHub and arXiv 找到。