Генерация последовательностей из скрытого пространства [pytorch]
У меня есть несколько вопросов о наилучшей практике использования рекуррентных сетей в pytorch
для генерации последовательностей.
Первый, если я хочу построить сеть декодера, я должен использовать nn.GRU
(или же nn.LSTM
) вместо nn.LSTMCell
(nn.GRUCell
)? Из моего опыта, если я работаю с LSTMCell
скорость расчетов значительно ниже (до 100 раз), чем если бы я использовал nn.LSTM
, Может быть, это связано с оптимизацией cudnn для LSTM
(а также GRU
) модуль? Есть ли способ ускорить LSTMCell
расчеты?
Я пытаюсь построить автоэнкодер, который принимает последовательности переменной длины. Мой автоэнкодер выглядит так:
class SimpleAutoencoder(nn.Module):
def init(self, input_size, hidden_size, n_layers=3):
super(SimpleAutoencoder, self).init()
self.n_layers = n_layers
self.hidden_size = hidden_size
self.gru_encoder = nn.GRU(input_size, hidden_size,n_layers,batch_first=True)
self.gru_decoder = nn.GRU(input_size, hidden_size, n_layers, batch_first=True)
self.h2o = nn.Linear(hidden_size,input_size) # Hidden to output
def encode(self, input):
output, hidden = self.gru_encoder(input, None)
return output, hidden
def decode(self, input, hidden):
output,hidden = self.gru_decoder(input,hidden)
return output,hidden
def h2o_apply(self,input):
return self.h2o(input)
Мой цикл тренировок выглядит так:
one_hot_batch = list(map(lambda x:Variable(torch.FloatTensor(x)),one_hot_batch))
packed_one_hot_batch = pack_padded_sequence(pad_sequence(one_hot_batch,batch_first=True).cuda(),batch_lens, batch_first=True)
_, latent = vae.encode(packed_one_hot_batch)
outputs, = vae.decode(packed_one_hot_batch,latent)
packed = pad_packed_sequence(outputs,batch_first=True)
for string,length,index in zip(*packed,range(batch_size)):
decoded_string_without_sos_symbol = vae.h2o_apply(string[1:length])
loss += criterion(decoded_string_without_sos_symbol,real_strings_batch[index][1:])
loss /= len(batch)
Обучение в такой манере, насколько я понимаю, - это сила учителя. Потому что на этапе декодирования сеть подает реальные входы (outputs,_ = vae.decode(packed_one_hot_batch,latent)
). Но для моей задачи это приводит к ситуации, когда на этапе тестирования сеть может очень хорошо генерировать последовательности, только если я использую реальные символы (как в режиме обучения), но если я передаю результаты предыдущего шага, сеть генерирует мусор (просто бесконечное повторение одного конкретного символа).
Я попробовал другой подход. Я генерировал "фальшивые" входные данные (только одни), чтобы модель генерировала только из скрытого состояния.
one_hot_batch_fake = list(map(lambda x:torch.ones_like(x).cuda(),one_hot_batch))
packed_one_hot_batch_fake = pack_padded_sequence(pad_sequence(one_hot_batch_fake, batch_first=True).cuda(), batch_lens, batch_first=True)
_, latent = vae.encode(packed_one_hot_batch)
outputs, = vae.decode(packed_one_hot_batch_fake,latent)
packed = pad_packed_sequence(outputs,batch_first=True)
Работает, но очень неэффективно, качество реконструкции очень низкое. Итак, второй вопрос, как правильно генерировать последовательности из латентного представления?
Я полагаю, что хорошей идеей является применение принуждения учителя с некоторой вероятностью, но для этого, как можно использовать слой nn.GRU, чтобы выходные данные предыдущего шага были входными данными для следующего шага?