【问题标题】:LSTM getting caught up in loopLSTM陷入循环
【发布时间】:2019-11-12 22:18:43
【问题描述】:

我最近实现了一个“从头开始”生成名称的 RNN,它运行良好,但远非完美。所以我想用 pytorch 的 LSTM 类试试运气,看看它是否有所作为。确实如此,并且前 7 ~ 8 个字符的输出看起来要好得多。但随后网络陷入循环并输出诸如“laulaulaulau”或“rourourourou”之类的内容(它应该生成法语名称)。

这是一个经常发生的问题吗?如果是这样,您知道解决方法吗?我担心网络不产生 EOS 代币的事实...... 这是一个已经在这里问过的问题Why does my keras LSTM model get stuck in an infinite loop? 但没有真正回答,因此我的帖子。

这是模型:

class pytorchLSTM(nn.Module):
    def __init__(self,input_size,hidden_size):
        super(pytorchLSTM,self).__init__()
        self.input_size = input_size
        self.hidden_size = hidden_size
        self.lstm = nn.LSTM(input_size, hidden_size)
        self.output_layer = nn.Linear(hidden_size,input_size)
        self.tanh = nn.Tanh()
        self.softmax = nn.LogSoftmax(dim = 2)

    def forward(self, input, hidden)
            out, hidden = self.lstm(input,hidden)
            out = self.tanh(out)
            out = self.output_layer(out)
            out = self.softmax(out)
        return out, hidden

输入和目标是两个one-hot编码向量序列,分别在开始和结束处有一个序列的开始和序列的结束向量。它们代表取自名称列表(数据库)的名称中的字符。 我在数据库中的每个名称上使用一个和标记。这是我使用的功能

def inputTensor(line):
#tensor starts with <start of sequence> token.
    tensor = torch.zeros(len(line)+1, 1, n_letters)
    tensor[0][0][n_letters - 2] = 1
    for li in range(len(line)):
        letter = line[li]
        tensor[li+1][0][all_letters.find(letter)] = 1
    return tensor

# LongTensor of second letter to end (EOS) for target
def targetTensor(line):
    letter_indexes = [all_letters.find(line[li]) for li in range(len(line))]
    letter_indexes.append(n_letters - 1) # EOS
    return torch.LongTensor(letter_indexes)

训练循环:


def train_lstm(model):
    start = time.time()
    criterion = nn.NLLLoss()
    optimizer = torch.optim.Adam(model.parameters())
    n_iters = 20000
    print_every = 1000
    plot_every = 500
    all_losses = []
    total_loss = 0
    for iter in range(1,n_iters+1):
        line = randomChoice(category_line)
        input_line_tensor = inputTensor(line)
        target_line_tensor = targetTensor(line).unsqueeze(-1)
        optimizer.zero_grad()       
        loss = 0
        output, hidden = model(input_line_tensor)
        for i in range(input_line_tensor.size(0)):
            l = criterion(output[i], target_line_tensor[i])
            loss += l
        loss.backward()
        optimizer.step() 

采样函数:

def sample():
    max_length = 20
    input = torch.zeros(1,1,n_letters)
    input[0][0][n_letters - 2] = 1
    output_name = ""
    hidden = (torch.zeros(2,1,lstm.hidden_size),torch.zeros(2,1,lstm.hidden_size)) 

    for i in range(max_length):
        output, hidden = lstm(input)
        output = output[-1][:][:]
        l = torch.multinomial(torch.exp(output[0]),num_samples = 1).item()
        if l == n_letters - 1:
            break
        else:
            letter = all_letters[l]
            output_name += letter
        input = inputTensor(letter)
    return output_name

典型的采样输出看起来像这样:

Laurayeerauerararauo
Leayealouododauodouo
Courouauurourourodau

你知道我该如何改进吗?

【问题讨论】:

  • 我很难理解您的 sample 函数。它应该做什么?创建名字?你能给出一些示例输出吗?它在哪里使用,在您的训练循环中/创建数据集/其他配置时?
  • sample 函数将 one-hot 编码的“序列开始”标记输入网络并要求输出,这是一个概率分布,从中抽取下一个字母。然后这个字母被馈送到网络,你重复这个过程,直到生成“序列结束”令牌。它在网络经过训练后用于生成名称。

标签: python-3.x neural-network pytorch lstm


【解决方案1】:

我找到了解释:

当使用 LSTM 类的实例作为 RNN 的一部分时,默认输入维度为 (seq_length,batch_dim,input_size)。为了能够将 lstm 的输出解释为概率(在输入集上),我需要在 Softmax 调用之前将其传递给 Linear 层,这就是问题发生的地方:Linear 实例期望输入格式为(batch_dim,seq_length,input_size)。

要解决此问题,需要在创建时将batch_first = True 作为参数传递给LSTM,然后向RNN 提供(batch_dim, seq_length, input_size) 形式的输入。

【讨论】:

  • 很高兴这对您的情况有所帮助,但它只是纠正错误的配置,并不能解决问题
【解决方案2】:

按重要性(和实施难易程度)顺序改进网络的一些技巧:

1。训练数据

如果您希望生成的样本看起来真实,则必须向网络提供一些真实数据。找到一组名称,将它们拆分为字母并转换为索引。仅这一步就会给出更真实的名字。

2。分开开始和结束标记。

我会选择&lt;SON&gt;(名称开头)和&lt;EON&gt;(名称结尾)。在这种配置中,神经网络可以学习到&lt;EON&gt; 的字母组合和&lt;SON&gt; 之后的字母组合。 ATM 它试图将两个不同的概念融入到这个自定义令牌中。

3。无监督保留

您可能希望为您的字母赋予一些语义含义,而不是一次性编码向量,请查看word2vec 了解基本方法。

基本上,每个字母将由N 维向量(例如 50 维)表示,并且如果该字母更频繁地出现在另一个字母旁边(a 更接近 k 而不是 @987654331 @)。

实现这一点的简单方法是获取一些文本数据集并尝试在每个时间步预测下一个字母。每个字母在开始时都会由随机向量表示,通过反向传播来更新字母表示以反映它们的相似性。

查看pytorch embedding tutorial了解更多信息。

4。不同的架构

您可能想查看 Andrej Karpathy 生成婴儿名字的想法。简单描述为here。

基本上,在训练之后,您可以为模型提供随机字母(例如 10 个)并告诉它预测下一个字母。

您从随机种子中删除最后一个字母并将预测的字母放在它的位置。迭代直到输出&lt;EON&gt;。

【讨论】:

  • 提示 1 和 2 已经是我实现的一部分(老实说,我没想到生成名称会那么难!我开始担心我实际上想用 RNN 实现什么。 ..) 我会研究提示 3 和 4,谢谢!
猜你喜欢
  • 2010-11-09
  • 1970-01-01
  • 2017-03-05
  • 2019-10-06
  • 1970-01-01
  • 2020-05-13
  • 2011-06-21
  • 2017-05-22
  • 2022-01-16
相关资源
最近更新 更多