Исчезновение seq_len в BiLSTM, основанном на внимании

Я изучаю несколько реализаций BiLSTM, основанного на самовнимании, и не понимаю, почему в каждой из них размер ввода и вывода различается. В частности, я имею в виду следующие коды, взятые из разных реализаций:

Реализация 1 e 2

 def attnetwork(self, encoder_out, final_hidden):
        # encoder_out shape = (batch_size, seq_len, n_hidden)
        # final_hidden shape = (1, batch_size, n_hidden)
        hidden = final_hidden.squeeze(0)  
        attn_weights = torch.bmm(encoder_out, hidden.unsqueeze(2)).squeeze(2)
        soft_attn_weights = F.softmax(attn_weights, 1)
        new_hidden = torch.bmm(encoder_out.transpose(1,2), soft_attn_weights.unsqueeze(2)).squeeze(2)
        return new_hidden # shape = (batch_size, n_hidden) 

Как вы можете видеть, эта реализация принимает в качестве входных данных два вектора размерности (batch_size, seq_len, n_hidden) и (1, batch_size, n_hidden), соответственно, и возвращает вектор размерности (batch_size, n_hidden) . Но где размер относительно seq_len? Мне нужен выходной вектор, равный входному (т.е. (batch_size, seq_len, n_hidden)).

Другая реализация, в которой размер ввода не совпадает с размером вывода:

def attention(self,H):
        M = torch.tanh(H) # Non-linear transformation size:(batch_size, hidden_dim, seq_len)
        a = F.softmax(torch.bmm(self.att_weight,M),dim=2) # a.Size : (batch_size,1,seq_len)
        a = torch.transpose(a,1,2) # (batch_size,seq_len,1)
        return torch.bmm(H,a) # (batch_size,hidden_dim,1)

Другая реализация с той же проблемой:

def attention(self, rnn_out, state):
        merged_state = torch.cat([s for s in state],1)
        merged_state = merged_state.squeeze(0).unsqueeze(2)
        # (batch, seq_len, cell_size) * (batch, cell_size, 1) = (batch, seq_len, 1)
        weights = torch.bmm(rnn_out, merged_state)
        weights = torch.nn.functional.softmax(weights.squeeze(2)).unsqueeze(2)
        # (batch, cell_size, seq_len) * (batch, seq_len, 1) = (batch, cell_size, 1)
        return torch.bmm(torch.transpose(rnn_out, 1, 2), weights).squeeze(2)

Как можно сделать вывод тензора того же размера, что и входной, без нарушения механизма самовнимания?

Спасибо!

РЕДАКТИРОВАТЬ: функция пересылки, которую я должен использовать, такова:

def forward(self, x, x_len):
        x = nn.utils.rnn.pack_padded_sequence(x, x_len, batch_first=True)
        out1, (h_n, c_n) = self.lstm1(x)
        # out1 = (seq_len, batch, num_directions * hidden_size)
        # h_n = (num_layers * num_directions, batch, hidden_size)
        x, lengths = nn.utils.rnn.pad_packed_sequence(out1, batch_first=True)
        x, att1 = self.atten1(x, lengths)  # skip connect
        return x

последний x в return x Мне абсолютно необходимо, чтобы он имел форму (batch_size, seq_len, hidden_state) (obv также в другом порядке, чтобы transpose было достаточно, чтобы исправить это).


person Elidor00    schedule 16.02.2021    source источник


Ответы (1)


По моему опыту, модель, основанная на внимании, делает следующее:

  1. Рассчитайте отношения между скрытыми состояниями декодера и выходами кодировщика.
  2. Возьмите softmax, чтобы получить распределение внимания.
  3. Возьмите взвешенную сумму вывода кодировщика, чтобы получить вывод внимания.

А модель Seq2seq действует так:

  1. Передайте свои последовательности, например, предложение через кодировщик, получите вывод кодировщика и скрытый слой, используйте этот скрытый слой для инициализации вашего декодера.
  2. Рассчитайте модель своего внимания по начальным скрытым состояниям декодера и выходам кодировщика.
  3. Загрузите в декодер начальный символ, например, получите один символ в качестве вывода и новое скрытое состояние, а затем используйте свою модель внимания для вычисления нового вывода.
  4. Подайте в декодер односимвольный ввод (определяется по вашему усмотрению, может зависеть от соотношения силы учителя) и вывод внимания (может объединять вывод внимания со скрытым состоянием декодера или чем-то еще), тогда вы получите новый символ.
  5. Цикл 4, чтобы получить символ за символом вашей последовательности предсказаний.

Вы можете взглянуть на это (со страницы 59): http://web.stanford.edu/class/cs224n/slides/cs224n-2021-lecture07-nmt.pdf

Каждый раз, когда символ отправляется в ваш декодер LSTM, он выводит новый символ, а его скрытые состояния обновляются до другого значения. Ключ в том, что скрытые состояния постоянно меняются.

Таким образом, вывод внимания (batch_size, hidden_dim, 1) является разумным, поскольку вы должны дать символ своей модели, использовать соответствующий вывод внимания, чтобы помочь предсказать следующий символ, затем получить новое скрытое состояние и получить новый вывод внимания и т. Д. . Вы не можете передать всю последовательность своему LSTM-декодеру, вы не можете использовать какие-либо скрытые состояния в этом случае.

person lunacancer    schedule 17.02.2021
comment
хорошо, я понимаю ваши рассуждения. Мы пропускаем обсуждение seq2seq, потому что у меня есть только один билст, к которому можно применить самовнимание (если вы действительно хотите, это похоже на самовнимание, используемое в кодировщике). Если результат (batch_size, hidden_dim, 1), как я могу использовать его для всей последовательности? Как я могу на практике повторить этот механизм столько раз, сколько seq_len? - person Elidor00; 17.02.2021
comment
При обучении LSTM необходимо группировать последовательности переменной длины. Например: если длина последовательностей в пакете размером 8 составляет [4,6,8,5,4,3,7,8], мне нужно каким-то образом отслеживать seq_len, даже когда обращаю внимание, но я не понимаю как. - person Elidor00; 17.02.2021
comment
О, вы спрашиваете о самовнимании ... Самовнимание - это механизм, который полностью отличается от кода, который вы вставили выше. Пожалуйста, погуглите. - person lunacancer; 17.02.2021
comment
По вашему мнению (ссылаясь на суть выше), правильно ли выводить weighted напрямую вместо representations? (используя weighted для остальной части обучения) - person Elidor00; 17.02.2021