Как обрабатывать вывод TransformerEncoderLayer в pytorch

#pytorch #bert-language-model #transformer

#pytorch #bert-language-model #transformer

Вопрос:

Я пытаюсь использовать встраивания предложений bio-bert для классификации текста более длинных фрагментов текста.

В его нынешнем виде я стандартизирую количество предложений в каждом фрагменте текста (некоторые предложения состоят только из («[PAD]») и прогоняю каждое предложение через biobert, чтобы получить векторы предложений, как это делается здесь: https://pypi.org/project/biobert-embedding /

Затем я запускаю эти вложения через преобразователь-преобразователь с 8 слоями и 16 заголовками внимания.

Преобразователь выводит что-то в форме (batch_size, num_sentences, embedding_size).

Затем я пытаюсь декодировать это с помощью линейного слоя и сопоставить его с моими классами (которых насчитывается 7) и смягчить вывод, чтобы получить вероятности.

Моя функция потерь — это просто nn.CrossEntropyLoss().

Сначала я суммировал размеры 1 выходных данных TransformerEncoder, чтобы получить что-то размером (batch_size, embedding_size). Это неизменно приводило к тому, что моя сеть сходилась на том, чтобы всегда с абсолютной уверенностью предсказывать одну из меток. Обычно наиболее распространенная метка в наборе данных.

Затем я попытался получить вывод только для последнего предложения вывода TransformerEncoder. т.е. TransformerEncoderOutput[:, -1, :] .

Это привело к чему-то подобному.

Затем я попытался запустить свой линейный слой на каждом из выходных данных TransformerEncoder, чтобы получить тензор размера (batch_size, num_sentences, 7). Затем я суммирую по dim 1 (создает тензор size (batch_size, 7) и softmax, как обычно. Идея здесь в том, что каждое предложение получает право голоса за метку после получения информации о его месте в последовательности.

Это еще быстрее привело к простому прогнозированию 1 для одной из меток и исчезающе малых значений для остальных.

Я чувствую, что я неправильно понимаю, как каким-то образом использовать вывод pytorch Transformer. Моя скорость обучения очень низкая, 0,00001, и это помогло задержать конвергенцию, но в конце концов она все равно сошлась.

Это наводит меня на мысль о том, что моя сеть неспособна что-либо понять в тексте и просто учится находить наиболее распространенные ярлыки. Я бы предположил, что это либо проблема с моей функцией потерь, либо проблема с тем, как я использую Transformer.

Есть ли явный недостаток в архитектуре, которую я изложил?

Ответ №1:

Таким образом, форма ввода и вывода преобразователя-кодировщика такова batch-size, sequence-length, embedding-size) . Существует три возможности обработки выходных данных преобразователя кодера (когда не используется декодер).

  1. вы берете среднее значение sequence-length измерения:
 x = self.transformer_encoder(x)
x = x.reshape(batch_size, seq_size, embedding_size)      
x = x.mean(1)
 
  1. подведите итог, как вы сказали:
 x = self.transformer_encoder(x)
x = x.reshape(batch_size, seq_size, embedding_size)      
x = x.sum(1)
 
  1. использование рекуррентной нейронной сети для объединения информации по sequence-length измерению:
 x = self.transformer_encoder(x)
x = x.reshape(batch_size, seq_len, embedding_size)      

# init hidden state
hidden = torch.zeros(layers, batch_size, embedding_size).to(device=device)  
x, hidden = self.rnn(x, hidden)
x = x.reshape(batch_size, seq_size, embedding_size)

# take last output
x = x[:, -1]
 

Я думаю, что использование последнего элемента вывода Transformer не является хорошей идеей. Потому что тогда вы берете только 1 / seq-len информации. Но при использовании rnn последний вывод по-прежнему содержит информацию о каждом другом выходе.

Я бы сказал, что лучше всего использовать среднее значение.

И к скорости обучения: для меня это всегда работало намного лучше, когда я использовал разминку. Если вы не знаете, что это такое: вы начинаете с низкой скорости обучения, например 0,00001, и увеличиваете ее, пока не достигнете некоторого целевого lr, например 0,002. И с этого момента вы просто разлагаете lr, как обычно.

Комментарии:

1. О, спасибо, отличный ответ. Усреднение сработало хорошо. Одна из проблем заключалась в том, что сначала я неправильно маскировал части последовательности [PAD]. Это действительно помогло.