我最近实现了一个“从头开始”生成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
输入和目标是两个单热编码向量的序列,分别在序列的开始和结束处分别有序列的开始和末端。它们代表从名称列表(数据库)获取的名称中的字符。 我对数据库中的每个名称使用和令牌。这是我使用的功能
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
你知道我该如何改善吗?
答案 0 :(得分:2)
我找到了解释:
在将LSTM
类的实例用作RNN的一部分时,默认输入维为(seq_length,batch_dim,input_size)
。为了能够将lstm的输出解释为概率(在一组输入中),我需要在Linear
调用之前将其传递到Softmax
层,这是发生问题的地方:{ {1}}实例期望输入的格式为Linear
。
要解决此问题,需要在创建时将(batch_dim,seq_length,input_size)
作为参数传递给batch_first = True
,然后将格式为LSTM
的输入提供给RNN。
答案 1 :(得分:0)
按重要性顺序(以及易于实施)改进网络的一些技巧:
如果要使生成的样本看起来真实,则必须向网络提供一些真实数据。查找一组名称,将其拆分为字母,然后转换为索引。仅此一步就会让出更现实的名称。
我会选择<SON>
(名称的开头)和<EON>
(名称的结尾)。在这种配置中,神经网络可以学习导致<EON>
的字母的组合和在<SON>
之后的字母的组合。在ATM上,它试图将两个不同的概念放入这个自定义令牌中。
您可能想给字母赋予一些语义,而不是使用一键编码的矢量,请检查word2vec以获得基本方法。
基本上,每个字母都将由N
维矢量(例如50个维)表示,并且如果该字母更频繁地出现在另一个字母(a
附近{{ 1}}比k
)。
一种简单的实现方法是获取一些文本数据集并尝试在每个时间步预测下一个字母。每个字母开头都将由随机向量表示,通过反向传播字母表示将被更新以反映它们的相似性。
检查pytorch embedding tutorial以获得更多信息。
您可能需要检查Andrej Karpathy关于生成婴儿名字的想法。简单描述here。
基本上,在训练之后,您会给模型填充随机字母(例如10),并告诉模型预测下一个字母。
您从随机种子中删除了最后一个字母,并将预测的字母放入了该位置。迭代直到输出x
。