【问题标题】:How can I use LSTM in pytorch for classification?如何在 pytorch 中使用 LSTM 进行分类?
【发布时间】:2018-05-21 10:38:01
【问题描述】:

我的代码如下:

class Mymodel(nn.Module):
    def __init__(self, input_size, hidden_size, output_size, num_layers, batch_size):
        super(Discriminator, self).__init__()
        self.input_size = input_size
        self.hidden_size = hidden_size
        self.output_size = output_size
        self.num_layers = num_layers
        self.batch_size = batch_size

        self.lstm = nn.LSTM(input_size, hidden_size)
        self.proj = nn.Linear(hidden_size, output_size)
        self.hidden = self.init_hidden()


    def init_hidden(self):
        return (Variable(torch.zeros(self.num_layers, self.batch_size, self.hidden_size)),
                Variable(torch.zeros(self.num_layers, self.batch_size, self.hidden_size)))

    def forward(self, x):
        lstm_out, self.hidden = self.lstm(x, self.hidden)
        output = self.proj(lstm_out)
        result = F.sigmoid(output)
        return result

我想使用 LSTM 将句子分类为好 (1) 或坏 (0)。使用此代码,我得到的结果是 time_step * batch_size * 1 但不是 0 或 1。如何编辑代码以获得分类结果?

【问题讨论】:

  • 你知道如何解决这个问题吗?@nnnmmm 我发现可能是 avg pool 可以提供帮助,但我不知道如何在这段代码中使用它?

标签: pytorch


【解决方案1】:

理论:

回想一下,LSTM 为序列中的每个输入输出一个向量。您正在使用句子,它们是一系列单词(可能转换为索引,然后嵌入为向量)。来自 LSTM PyTorch tutorial 的这段代码清楚地表明了我的意思(***强调我的意思):

lstm = nn.LSTM(3, 3)  # Input dim is 3, output dim is 3
inputs = [autograd.Variable(torch.randn((1, 3)))
          for _ in range(5)]  # make a sequence of length 5

# initialize the hidden state.
hidden = (autograd.Variable(torch.randn(1, 1, 3)),
          autograd.Variable(torch.randn((1, 1, 3))))
for i in inputs:
    # Step through the sequence one element at a time.
    # after each step, hidden contains the hidden state.
    out, hidden = lstm(i.view(1, 1, -1), hidden)

# alternatively, we can do the entire sequence all at once.
# the first value returned by LSTM is all of the hidden states throughout
# the sequence. the second is just the most recent hidden state
# *** (compare the last slice of "out" with "hidden" below, they are the same)
# The reason for this is that:
# "out" will give you access to all hidden states in the sequence
# "hidden" will allow you to continue the sequence and backpropagate,
# by passing it as an argument  to the lstm at a later time
# Add the extra 2nd dimension
inputs = torch.cat(inputs).view(len(inputs), 1, -1)
hidden = (autograd.Variable(torch.randn(1, 1, 3)), autograd.Variable(
torch.randn((1, 1, 3))))  # clean out hidden state
out, hidden = lstm(inputs, hidden)
print(out)
print(hidden)

再来一次:比较最后一段“out”和下面的“hidden”,它们是一样的为什么?嗯……

如果您熟悉 LSTM,我现在推荐 PyTorch LSTM docs。在输出部分下,通知 h_t 在每个 t 处输出。

现在,如果您不习惯 LSTM 样式的方程,请查看 Chris Olah 的 LSTM blog post。向下滚动到展开的网络图:

当您逐字输入句子时 (x_i-by-x_i+1),您会从每个时间步得到一个输出。您想解释整个句子以对其进行分类。所以你必须等到 LSTM 看到所有的单词。也就是说,您需要取h_t,其中t 是您句子中的单词数。

代码:

这是一个编码reference。我不会复制粘贴整个内容,只复制相关部分。奇迹发生在self.hidden2label(lstm_out[-1])

class LSTMClassifier(nn.Module):

    def __init__(self, embedding_dim, hidden_dim, vocab_size, label_size, batch_size):
        ...
        self.word_embeddings = nn.Embedding(vocab_size, embedding_dim)
        self.lstm = nn.LSTM(embedding_dim, hidden_dim)
        self.hidden2label = nn.Linear(hidden_dim, label_size)
        self.hidden = self.init_hidden()

    def init_hidden(self):
        return (autograd.Variable(torch.zeros(1, self.batch_size, self.hidden_dim)),
                autograd.Variable(torch.zeros(1, self.batch_size, self.hidden_dim)))

    def forward(self, sentence):
        embeds = self.word_embeddings(sentence)
        x = embeds.view(len(sentence), self.batch_size , -1)
        lstm_out, self.hidden = self.lstm(x, self.hidden)
        y  = self.hidden2label(lstm_out[-1])
        log_probs = F.log_softmax(y)
        return log_probs

【讨论】:

  • 不应该是:`y = self.hidden2label(self.hidden[-1])
  • @RameshK lstm_out 是每个时间步的隐藏状态。 lstm_out[-1] 是最终的隐藏状态。 self.hidden 是最终隐藏向量和单元向量 (h_f, c_f) 的 2 元组。忽略任何必要的重塑,您可以使用self.hidden[0]。掩蔽和双向性存在细微差别,所以通常我会说self.hidden[0] 是首选,但在这里它真的没关系。
  • 谢谢,但仍不确定。 pytorch 文档说:- **h_n** of shape (num_layers * num_directions, batch, hidden_​​size): tensor containing the hidden state for t = seq_len. 其中输出为Outputs: output, (h_n, c_n)
  • 如何修改它以用于非 nlp 设置?我有一个脉冲(一系列向量)的时间序列数据,并且想要将一系列向量分类为 1 或 0? Embedding_dim 只是输入 dim?
  • @donkey 可能应该是它自己的问题,但您可以删除单词嵌入并将数据直接输入self.lstm,确保它具有与 x 相同的形状:(sequence length, batch size, vector dimension)
【解决方案2】:

您需要弄清楚的主要问题是在准备数据时应该将批量大小放在哪个暗处。据我所知,如果你没有在你的 nn.LSTM() 初始化函数中设置它,它会自动假设第二个暗淡是你的批量大小,这与其他 DNN 框架相比有很大不同。也许你可以试试:

self.lstm = nn.LSTM(input_size, hidden_size, num_layers, batch_first=True)

这样要求您的模型将您的第一个暗淡视为批量暗淡。

【讨论】:

    【解决方案3】:

    作为最后一层,你必须有一个线性层,无论你想要多少类,即 10 个,如果你像 MNIST 那样进行数字分类。对于您的情况,因为您正在进行是/否(1/0)分类,所以您有两个标签/类,因此您的线性层有两个类。我建议添加一个线性层作为

    nn.Linear (feature_size_from_previous_layer, 2)

    然后使用交叉熵损失训练模型。

    标准 = nn.CrossEntropyLoss()

    优化器 = optim.SGD(net.parameters(), lr=0.001, 动量=0.9)

    【讨论】:

    • 但是我的代码已经有了一个线性层。问题是当程序在这行'output = self.proj(lstm_out)'上运行时,有一条关于我之前提到的不匹配demension的错误消息。@Manoj Acharya
    • 您可能需要重新调整到正确的尺寸。对张量使用 .view 方法。
    猜你喜欢
    • 2018-04-05
    • 2019-09-05
    • 2022-10-01
    • 2023-04-05
    • 2020-07-11
    • 2020-01-04
    • 1970-01-01
    • 2021-03-05
    • 2021-05-09
    相关资源
    最近更新 更多