#pytorch #lstm #recurrent-neural-network
#pytorch #lstm #рекуррентная нейронная сеть
Вопрос:
Я построил языковую модель Bi-LSTM с помощью pytorch
и обнаружил, что после 200 эпох модель внезапно вернула только бессмысленные токены с потерей Nan, в то время как раньше она возвращала разумные токены.
Пожалуйста, обратитесь к коду модели ниже:
# optimizer = torch.optim.Adam(model.parameters(), lr=0.009, amsgrad=False)
class BiLSTM(nn.Module):
def __init__(self, voc_size, hidn_size, emb_size=300):
super().__init__()
self.voc_size = voc_size
self.emb_size = emb_size
self.hidn_size = hidn_size
self.emb = nn.Embedding(num_embeddings=voc_size, embedding_dim=emb_size)
self.lstm = nn.LSTM(input_size=emb_size, hidden_size=hidn_size, bidirectional=True)
self.lm_out = nn.Linear(hidn_size*2, voc_size)
self.dropout = nn.Dropout(p=0.3)
def forward(self, x, prev_state):
state_h, state_c = prev_state
bs = len(x)
emb = self.emb(x)
emb = emb.permute(1,0,-1)
out, (state_h, state_c) = self.lstm(emb, (state_h[:,:bs,:].contiguous(), state_c[:,:bs,:].contiguous()))
forward_out = out[:, :, :self.hidn_size]
backward_out = out[:, :, self.hidn_size:]
concat_h = torch.cat([forward_out[:-2], backward_out[2:]], dim=2)
final_out = self.lm_out(self.dropout(concat_h.permute(1,0,2)))
return final_out.view(final_out.size()[0]*final_out.size()[1], final_out.size()[-1]), (state_h, state_c)
Ответ №1:
Проблема была вызвана exploding gradient
. Я нашел это, проверив градиент и вес слоя:
model.lm_out.weight
>>>
tensor([[nan, nan, nan, ..., nan, nan, nan],
[nan, nan, nan, ..., nan, nan, nan],
[nan, nan, nan, ..., nan, nan, nan],
...,
[nan, nan, nan, ..., nan, nan, nan],
[nan, nan, nan, ..., nan, nan, nan],
[nan, nan, nan, ..., nan, nan, nan]],
device='cuda:0', requires_grad=True)
model.lm_out.weight.grad
>>>
tensor([[nan, nan, nan, ..., nan, nan, nan],
[nan, nan, nan, ..., nan, nan, nan],
[nan, nan, nan, ..., nan, nan, nan],
...,
[nan, nan, nan, ..., nan, nan, nan],
[nan, nan, nan, ..., nan, nan, nan],
[nan, nan, nan, ..., nan, nan, nan]],
device='cuda:0', requires_grad=True)
Поэтому я редактирую свой код следующим образом:
loss.backward()
torch.nn.utils.clip_grad_norm_(model.parameters(), 1)
optimizer.step()
Это решило мою проблему.
Комментарии:
1. Вы уверены, что хотите, чтобы ваш LM действительно генерировал
pad
токены? Если это допустимый вывод, вы усложняете обучение сети, учитывая, что у вас, вероятно, много этих токенов и что они, скорее всего, будут предсказаны. Обычно вы просто выполняете выборку слов в цикле доend
токена и избегаете создания фактических пэдов.2. @runDOSrun Спасибо за ваш совет. Я не рассматривал
pad
, и ваш комментарий заставляет меня понять, что неправильно этого не делать. Я ввожу один полный обзор IMDB (набор предложений), в этом случае, я думаю, мне следовало бы: 1) добавить токен <eos> в конце каждого ввода данных обзора, 2) не рассчитанные потери сгенерированных токенов после <eos> , 3) и, кроме того, я должен был выбрать некоторыетокены из сгенерированного обзора для расчета потерь, это правильно?3. Да, это, безусловно, было бы правильно для вашего принятого подхода! Есть и другие очень разные способы, которыми можно это сделать, но это зависит от того, какой тип текста вы хотите сгенерировать и его структуры. Например, для обычного LM вам не нужна структура предложений в пакетах. Если вы прогнозируете слово за словом, ввод также может быть на уровне слова, так что вам не нужны дополнения (например, путем ввода всего документа или его случайных фрагментов). Затем позиция <eos> (конец документа / отправлено) может использоваться для указания структуры вывода.
4. @runDOSrun Спасибо за ваше руководство. Теперь я могу понять, как правильно кодировать LM 🙂