PyTorch n-to-1 LSTM ничего не изучает - PullRequest
0 голосов
/ 30 августа 2018

Я новичок в 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 итерациях и, следовательно, будет только предсказывать этот класс. Другая причина также может заключаться в том, что я в настоящее время использую мини-партии только одного образца, потому что я еще не выяснил, как использовать другие размеры.

1 Ответ

0 голосов
/ 12 февраля 2019

Потому что вы пытаетесь использовать целое предложение для классификации, поэтому следующая строка:

self.hidden2tag(lstm_out.view(len(sentence), -1))

должен быть изменен на, чтобы он доставлял последние классификаторы в классификатор.

self.hidden2tag(lstm_out.view(sentence[-1], -1))

Но я также не уверен, так как я не знаком с LSTM.

...