LSTM 陷入循环

Joh*_*owk 3 neural-network python-3.x lstm pytorch

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

这是一个经常发生的问题吗?如果是这样,您知道解决方法吗?我担心网络不产生 EOS 令牌的事实......这是一个已经在这里问过的问题为什么我的 keras LSTM 模型陷入无限循环? 但没有真正回答所以我的帖子。

这是模型:

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
Run Code Online (Sandbox Code Playgroud)

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

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)
Run Code Online (Sandbox Code Playgroud)

训练循环:


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() 
Run Code Online (Sandbox Code Playgroud)

采样函数:

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
Run Code Online (Sandbox Code Playgroud)

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

Laurayeerauerararauo
Leayealouododauodouo
Courouauurourourodau
Run Code Online (Sandbox Code Playgroud)

你知道我该如何改进吗?

Joh*_*owk 5

我找到了解释:

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

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