Я пытаюсь получить контроль над LSTM и pytorch. Даны последовательности различной длины. Каждый элемент данных в последовательности состоит из 8 объектов, и каждый элемент данных принадлежит одному из 6 классов (0-5). Я хочу узнать, как использовать LSTM для прогнозирования меток этих точек данных.
Пока я предпринял пару попыток решить эту проблему, но боюсь, что мне не хватает некоторых базовых знаний, когда речь заходит об этой теме.
Я начал с дополнения последовательностей -1 и меток до max_amount.
У меня есть загрузчики данных (train, test, val), которые заботятся о доставке пакетов формы [batch_size, max_seq_length, num_features]. Модель довольно проста и состоит из lstm и линейного слоя.
LSTM:
...
self.lstm = nn.LSTM(
input_size=self.sequence_dimension, # 8 features
hidden_size=self.hidden_dim,
num_layers=self.n_layers,
batch_first=True,
)
self.linear_fc = nn.Linear(self.hidden_dim, self.output_size)
...
init:
def init_hidden(self):
# the weights are of the form (nb_layers, batch_size, nb_lstm_units)
hidden_a = torch.randn(self.n_layers, self.batch_size, self.hidden_dim)
hidden_b = torch.randn(self.n_layers, self.batch_size, self.hidden_dim)
hidden_a = Variable(hidden_a)
hidden_b = Variable(hidden_b)
return (hidden_a, hidden_b)
вперед:
def forward(self, X):
# shape of X: [batch_size, max_seq_len, feature_size]
self.hidden = self.init_hidden()
# get unpadded sequence lenghts (padding: -1)
lenghts = []
for batch in X:
for i, elem in enumerate(batch):
if elem[0] == -1:
lenghts.append(i-1)
break
# pack the padded sequences, length contains unpadded lenghts (eg., [43,46,67,121]
x_packed = torch.nn.utils.rnn.pack_padded_sequence(X, lenghts, batch_first=True, enforce_sorted=False)
lstm_out, self.hidden = self.lstm(x_packed.float(), self.hidden)
# unpack
x_unpacked, seq_len = torch.nn.utils.rnn.pad_packed_sequence(lstm_out, batch_first=True)
#squash the batches from [batch_size, max_seq_len_of_this_batch, hidden_dim]
#to [batch_size*max_seq_len_of_this_batch, hidden dim]
batches_squashed = x_unpacked.contiguous().view(-1, x_unpacked.shape[2])
#feed it to linear
y_pred = self.linear_fc(batches_squashed)
#unsquash batches to [batch_size, max_seq_len_of_this_batch, hidden_dim]
y_pred = y_pred.contiguous().view((batch_size, max(lenghts),self.output_size))
return y_pred, max(lenghts)
Я очень не уверен, правильно ли я это делаю, особенно с линейным слоем. Он получает тензор формы [batchsize * max_seq_len, hidden_layers], это правильно? Там еще есть дополненные значения ...
В моем цикле поезда я просто инициирую скрытое состояние для каждой эпохи, а затем зацикливаю (ввод, метки) из загрузчика данных:
loss_funtion = nn.CrossEntropyLoss(ignore_index=-1)
optimizer = torch.optim.SGD(net.parameters(), lr=lr, momentum=0.9)
for inputs, labels in train_loader:
counter+=1
net.zero_grad()
if train_on_gpu:
inputs = inputs.to(device)
labels = labels.to(device)
output, _ = net(inputs)
#remove excessive padding for labels
labels = decreasing_padding(labels, max_padding_for_this_batch)
#transposing the output to fit the crossEntropy definiton
loss = loss_funtion(output.transpose(1, 2),labels.long())
loss.backward()
optimizer.step()
LSTM часто просто выводит одну метку для всей последовательности (есть последовательности, для которых это правильно). Потеря и точность в обучении и проверочный набор указывают на то, что он не обучается. Хотя потери немного уменьшаются, они сильно колеблются
Я подозреваю, что линейный слой реализован неправильно.
- Должен ли я проходить каждое скрытое состояние (без заполнения?) Один за другим через слой и складывать его выходы?
Я был бы чрезвычайно признателен, если бы кто-то мог указать на мои ошибки и заблуждения, я потратил довольно много времени на это до сих пор.