PyTorch 中的 LSTM 分类名称

Hec*_*zos 4 python lstm pytorch

我正在尝试https://pytorch.org/tutorials/intermediate/char_rnn_classification_tutorial.html中提供的示例,但我使用的是 LSTM 模型而不是 RNN。该数据集由不同的名称(不同大小)及其对应的语言(语言总数为18种)组成,目标是训练一个模型,给定某个名称输出其所属的语言。

我现在的问题是:

  • 如何在 LSTM 中处理可变大小的名称,即 Hector 和 Kim
  • 每次在 LSTM 中都会处理整个名称(字符的连续性),因此 softmax 函数的输出具有形状,(#characters of name, #target classes)但我只想获取形状(1,#target of classes),以便确定每个名称对应于哪个类。我试图只得到最后一行,但结果非常糟糕。
class LSTM(nn.Module):

    def __init__(self, embedding_dim, hidden_dim, vocab_size, tagset_size):
        super(LSTM, self).__init__()
        self.hidden_dim = hidden_dim

        self.word_embeddings = nn.Embedding(vocab_size, embedding_dim)

        # The LSTM takes word embeddings as inputs, and outputs hidden states
        # with dimensionality hidden_dim.
        self.lstm = nn.LSTM(embedding_dim, hidden_dim)

        # The linear layer that maps from hidden state space to tag space
        self.hidden2tag = nn.Linear(hidden_dim, tagset_size)
        self.softmax = nn.LogSoftmax(dim = 1)


    def forward(self, word):
        embeds = self.word_embeddings(word)
        lstm_out, _ = self.lstm(embeds.view(len(word), 1, -1))
        tag_space = self.hidden2tag(lstm_out.view(len(word), -1))
        tag_scores = self.softmax(tag_space)
        return tag_scores

    def initHidden(self):
        return Variable(torch.zeros(1, self.hidden_dim))
Run Code Online (Sandbox Code Playgroud)
    lstm = LSTM(n_embedding_dim,n_hidden,n_characters,n_categories)
    optimizer = torch.optim.SGD(lstm.parameters(), lr=learning_rate)
    criterion = nn.NLLLoss()
    def train(category_tensor, line_tensor):
        # i.e. line_tensor = tensor([37,  4, 14, 13, 19,  0, 17,  0, 10,  8, 18]) and category_tensor = tensor([11])
        optimizer.zero_grad()
        output = lstm(line_tensor)

        loss = criterion(output[-1:], category_tensor) # VERY BAD
        loss.backward()

        optimizer.step()

        return output, loss.data.item()
Run Code Online (Sandbox Code Playgroud)

其中line_tensor 是可变大小(取决于每个名称的大小)并且 是字符与其在字典中的索引之间的映射

muj*_*iga 5

让我们一步步深入研究解决方案

框架问题

根据您的问题陈述,您将必须使用 LSTM 进行分类,而不是使用典型的标记。LSTM 在一定的时间步长内展开,这就是循环模型的输入和输出维度为

  • 输入:batch size X time steps X input size
  • 输出:batch size X time steps X hidden size

现在既然你想用它来分类,你有两个选择:

  1. 在所有时间步/展开的输出上放置一个密集层[我下面的示例使用这个]
  2. 忽略除最后一个之外的所有时间步输出,并在最后一个时间步上放置一个密集层

因此,我们 LSTM 模型的输入是每个 LSTM 时间步长作为一个字符输入的名称,输出将是与其语言相对应的类。

如何处理可变长度输入/名称

我们这里又有两个选择。

  1. 将相同长度的名称批量在一起。这称为分桶
  2. 根据您拥有的名称的平均大小确定最大长度。填充较小的名称并删除较长的名称 [下面的示例使用的最大长度为 10]

我们需要嵌入层吗?

不会。嵌入层通常用于学习单词的良好向量表示。但在字符模型的情况下,输入是字符而不是单词,因此添加嵌入层没有帮助。字符可以直接编码为数字,嵌入层在捕获不同字符之间的关系方面作用很小。您仍然可以使用嵌入层,但我坚信它不会有帮助。

玩具角色LSTM模型代码

import numpy as np
import torch
import torch.nn as nn

# Model architecture 
class Recurrent_Model(nn.Module):
    def __init__(self, output_size, time_steps=10):
        super(Recurrent_Model, self).__init__()
        self.time_steps = time_steps
        self.lstm = nn.LSTM(1,32, bidirectional=True, num_layers=2)
        self.linear = nn.Linear(32*2*time_steps, output_size)

    def forward(self, x):        
        lstm_out, _ = self.lstm(x)
        return self.linear(lstm_out.view(-1,32*2*self.time_steps))

# Sample input and output
names = ['apple', 'dog', 'donkey', "elephant", "hippopotamus"]
lang = [0,1,2,1,0]

def pad_sequence(name, max_len=10):
    x = np.zeros((len(name), max_len))
    for i, name in enumerate(names):
        for j, c in enumerate(name):
            if j >= max_len:
                break
            x[i,j] = ord(c)
    return torch.FloatTensor(x)

x = pad_sequence(names)
x = torch.unsqueeze(x, dim=2)
y = torch.LongTensor(lang)

model = Recurrent_Model(3)
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), 0.01)

for epoch in range(500):
    model.train()
    output = model(x)
    loss = criterion(output, y)
    print (f"Train Loss: {loss.item()}")
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
Run Code Online (Sandbox Code Playgroud)

笔记

  1. 所有张量都会加载到内存中,因此如果您有庞大的数据集,则必须使用数据集和数据加载器来避免 OOM 错误。
  2. 您必须将数据拆分为训练测试并在测试数据集上进行验证(标准模型构建内容)
  3. 在将输入张量传递给模型之前,您必须对其进行标准化(同样是标准模型构建内容)

最后

那么如何确保您的模型架构没有错误或正在学习。正如 Andrej karpathy 所说,在小数据集上过度拟合模型,如果过度拟合,那么我们就可以了。