我正在尝试使用 torch.autograd 训练一个简单的循环神经网络,该网络可以预测代表 ABC 表示法中的歌曲的字符序列中的下一个字符。
模型看起来像这样:
model = keras.Sequential([
keras.layers.Input(shape=(SEQ_LENGTH,), batch_size=batch_size),
keras.layers.Embedding(len(vocabulary), 256),
keras.layers.LSTM(1024, return_sequences=True, stateful=stateful),
keras.layers.Dense(len(vocabulary))
])
训练过程如下:
loss_fn = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=5e-3)
for i in range(1000):
inputs, targets = random_inputs_and_targets(vectorized_songs, seq_length=SEQ_LENGTH, batch_size=BATCH_SIZE)
predictions = model(inputs)
loss = loss_fn(predictions.permute((0, 2, 1)), torch.from_numpy(targets).long())
loss.backward()
optimizer.step()
optimizer.zero_grad()
然后我保存模型参数并将它们加载到类似的模型中,但是这次模型是有状态的并且具有批量大小
1
:
torch.save(model.state_dict(), os.path.join(cwd, "model.pt"))
trained_model = build_model(1, True)
trained_model.load_state_dict(torch.load(os.path.join(cwd, "model.pt")))
trained_model.eval()
然后,我使用加载的模型来预测一串字符,我希望这些字符看起来像 ABC 表示法中的歌曲:
input_eval = [char_to_index[s] for s in start_string]
input_eval = torch.unsqueeze(torch.tensor(input_eval), 0)
text_generated = []
for i in range(generation_length):
predictions = torch.squeeze(model(input_eval), 0)
predicted_index = torch.multinomial(softmax(predictions, dim=0), 1, replacement=True)[-1, 0]
input_eval = torch.unsqueeze(torch.unsqueeze(predicted_index, 0), 0)
text_generated.append(index_to_char[predicted_index.item()])
return start_string + ''.join(text_generated)
完整代码在这里。
在 1000 个训练周期中,损失函数值从
4.42
左右下降到 0.78
,正如预期的那样。
但是当我尝试使用“经过训练的”模型生成歌曲时,结果看起来像一个随机字符串:
XwQ5>ab>6q6S(z']!<hxaG4..M= (=ERp/xJmS|qIh_CzbM0D-N 6Yc=Ei[tcodBsEKfW<WZ5Jb("u1rrGLcFIk"PVk.'FEII:(qu7.nFbw^3/RY2LyrW
。完整结果的示例可以在此处看到。
我该如何开始调试出现的问题?之前我使用 torch.autograd
构建了
一个简单的非循环分类器,它的输出准确率只有 90%,但这仍然比我尝试构建 RNN 时要好得多。会不会是 RNN 预测下一个字符所需的隐藏状态在训练或实际预测过程中某个地方丢失了?
欢迎任何建议,因为我陷入困境。
设法找到问题。打算在这里发布答案以供参考。
问题出在推论上。 Keras模型输出log softmax,所以为了得到概率分布,我需要找到模型输出的指数,即
predictions.exp()
。
但是,我在输出上错误地调用了 softmax (
torch.nn.functional.softmax(predictions, dim=0)
)。