Я новичок в PyTorch и LSTM, и я пытаюсь обучить классификационную модель, которая принимает предложения, в которых каждое слово кодируется через word2vec (предварительно обученные векторы), и выводит один класс после того, как оно увидело полное предложение. У меня есть четыре разных класса. Предложения имеют переменную длину.
Мой код выполняется без ошибок, но он всегда предсказывает один и тот же класс, независимо от того, сколько эпох я обучаю своей модели. Так что я думаю, что градиенты не имеют должного распространения. Вот мой код:
class LSTM(nn.Module):
def __init__(self, embedding_dim, hidden_dim, tagset_size):
super(LSTM, self).__init__()
self.hidden_dim = hidden_dim
self.lstm = nn.LSTM(embedding_dim, hidden_dim)
self.hidden2tag = nn.Linear(hidden_dim, tagset_size)
self.hidden = self.init_hidden()
def init_hidden(self):
# The axes semantics are (num_layers, minibatch_size, hidden_dim)
return (torch.zeros(1, 1, self.hidden_dim).to(device),
torch.zeros(1, 1, self.hidden_dim).to(device))
def forward(self, sentence):
lstm_out, self.hidden = self.lstm(sentence.view(len(sentence), 1, -1), self.hidden)
tag_space = self.hidden2tag(lstm_out.view(len(sentence), -1))
tag_scores = F.log_softmax(tag_space, dim=1)
return tag_scores
EMBEDDING_DIM = len(training_data[0][0][0])
HIDDEN_DIM = 256
model = LSTM(EMBEDDING_DIM, HIDDEN_DIM, 4)
model.to(device)
loss_function = nn.NLLLoss()
optimizer = optim.SGD(model.parameters(), lr=0.1)
for epoch in tqdm(range(n_epochs)):
for sentence, tag in tqdm(training_data):
model.zero_grad()
model.hidden = model.init_hidden()
sentence_in = torch.tensor(sentence, dtype=torch.float).to(device)
targets = torch.tensor([label_to_idx[tag]], dtype=torch.long).to(device)
tag_scores = model(sentence_in)
res = torch.tensor(tag_scores[-1], dtype=torch.float).view(1,-1).to(device)
# I THINK THIS IS WRONG???
print(res) # tensor([[-10.6328, -10.6783, -10.6667, -0.0001]], device='cuda:0', grad_fn=<CopyBackwards>)
print(targets) # tensor([3], device='cuda:0')
loss = loss_function(res, targets)
loss.backward()
optimizer.step()
Код в значительной степени вдохновлен https://pytorch.org/tutorials/beginner/nlp/sequence_models_tutorial.html
Разница в том, что у них есть модель от последовательности к последовательности, а у меня модель от последовательности к ОДНОЙ.
Я не уверен, в чем проблема, но я предполагаю, что оценки, возвращаемые моделью, содержат оценку для каждого тега, а моя основная правда содержит только индекс правильного класса? Как это будет правильно обрабатываться?
Или функция потерь, возможно, не подходит для моего случая использования? Также я не уверен, что это сделано правильно:
res = torch.tensor(tag_scores[-1], dtype=torch.float).view(1,-1).to(device)
Взяв tag_scores[-1]
Я хочу получить оценки после того, как последнее слово было передано в сеть, потому что tag_scores содержит оценки после каждого шага, если я правильно понимаю.
И вот как я оцениваю:
with torch.no_grad():
preds = []
gts = []
for sentence, tag in tqdm(test_data):
inputs = torch.tensor(sentence, dtype=torch.float).to(device)
tag_scores = model(inputs)
# find index with max value (this is the class to be predicted)
pred = [j for j,v in enumerate(tag_scores[-1]) if v == max(tag_scores[-1])][0]
print(pred, idx_to_label[pred], tag)
preds.append(pred)
gts.append(label_to_idx[tag])
print(f1_score(gts, preds, average='micro'))
print(classification_report(gts, preds))
EDIT
При перетасовке данных перед тренировкой это работает. Но почему?
РЕДАКТИРОВАТЬ 2 :
Я думаю, что причина необходимости перетасовки заключается в том, что мои тренировочные данные содержат образцы для каждого класса в группах. Поэтому, обучая их друг за другом, модель будет видеть только один и тот же класс в последних N итерациях и, следовательно, будет только предсказывать этот класс. Другая причина также может заключаться в том, что я в настоящее время использую мини-партии только одного образца, потому что я еще не выяснил, как использовать другие размеры.