Понимание трансформеров через реализацию и визуализацию весов внимания путем анализа промежуточных слоев модели.

Было показано, что трансформеры представляют собой отличную архитектуру нейронной сети и хорошо работают в различных областях, особенно зарекомендовав себя в области NLP и Vision. Это архитектура кодер-декодер с блоком кодировщика, генерирующим ключи и значения для блока декодера посредством многоголового внутреннего внимания, и блоком декодера, генерирующим запросы через маскированный многоголовый блок внутреннего внимания. Затем пара ключ-значение от кодировщика и запрос от декодера проходят через другое многоголовое внутреннее внимание (фактически перекрестное внимание) для создания выходных векторов вероятности.

Механизм внимания задается уравнением:

где √d — это шкала, используемая для нормализации dot-product для стабилизации обратного распространения через функцию softmax даже при очень большой величине dot-product. d в уравнении — это размерность query и keys. Это также известно как Scaled Dot-Product Attention. В Multi-Head Attention мы просто проецируем ввод через h блоков внимания вместо одного. Это позволяет сети изучить h различных функций внимания, где каждая из них может сосредоточиться на определенном свойстве данных. Это описывается уравнением:

При желании это многоголовое внимание может использовать маску, полезную для маскировки будущих элементов в последовательности, чтобы запретить сети обманывать в таких задачах, как предсказание следующего слова. Мы устанавливаем маскируемые элементы очень маленькими, например. −∞, что приводит к значению, близкому к 0, после операции softmax.

Теперь, когда мы знаем, что такое Multi-Head Attention, давайте попробуем реализовать это в pytorch.

Теперь давайте немного уменьшим масштаб, чтобы сосредоточиться на кодирующей части Transformers. Сюда входят два дополнительных блока — Add & Norm и Feed Forward. Add & Norm обеспечивает остаточное соединение, за которым следует нормализация уровня, в то время как Feed Forward представляет собой простую, позиционную, полностью связанную сеть с прямой связью.

Убрав кодировщик, давайте теперь рассмотрим реализацию декодера, которая очень похожа на кодировщик, за исключением дополнительного Masked Multi-Headed Self-Attention, выход которого служит вектором value для следующего Multi-Headed Self-Attention, а векторы query и key поступают из выхода кодера. .

На данный момент мы определили кодировщик и декодер Transformers. В качестве следующего шага нам нужно объединить их в полную архитектуру Transformers. Еще одна концепция, которую важно понять, — это Positional Embedding, которая обычно добавляется к входному вектору, чтобы понять порядок входных последовательностей, поскольку преобразователи являются инвариантными по положению. Обычно для этого позиционного кодирования используется комбинация функций синуса и косинуса, но можно также использовать обучаемое позиционное кодирование, что мы и будем использовать в нашем примере. Здесь имеется в виду приложение для классификации цифр MNIST, и в этом случае длина последовательности всегда будет одинаковой, и нам не нужно маскировать ввод декодера — вместо этого мы можем их изучить. При обработке естественного языка вы также изучаете вложение для каждого подслова через слой Input Embedding, который проецирует однократный вектор в вектор слова с более низкой размерностью. Мы можем заменить его линейным слоем, чтобы он проецировал значение каждого пикселя в вектор большего размера.

Оригинальные трансформаторы, представленные Vaswani et al. В статье Внимание — это все, что вам нужно (https://arxiv.org/abs/1706.03762) содержится encoder-decoder архитектура. Однако несколько рабочих потоков исследуют encoder-only или decoder-only архитектуру, которая лучше справляется с конкретными задачами и требует меньше памяти и времени для обучения. Примером архитектуры только для кодировщика является BERT, в котором определенные токены маскируются на входе, а перед сетью ставится задача предсказать эти отсутствующие токены. Точно так же GPT-n является примером архитектуры только для декодера, где, учитывая прошлое n

токенов, сеть обучена предсказывать (n+1)-й токен. Для рассматриваемого здесь простого примера классификации изображений архитектура только для кодировщика будет работать довольно хорошо, однако здесь я создаю архитектуру encoder-decoder, а также добавляю поддержку архитектуры encoder-only для полного понимания того, как построить и обучить такую ​​систему. . Я тренирую обе сети в этом блокноте и визуализирую уровни внимания.

Давайте определим набор данных MNIST и классы загрузчика данных; а также функцию для вычисления точности предсказания.

На данный момент мы определили модель и набор данных. Последним шагом в этом процессе является определение конвейера обучения и проверки модели.

Мы также заинтересованы в визуализации уровней внимания, чтобы понять, на что обращает внимание каждый пиксель на каждом уровне кодировщика/декодера. Давайте определим эти функции визуализации.

Итак, теперь мы готовы обучить нашу модель и визуализировать веса внимания. Обучение варианту полных преобразователей (кодер-декодер) с 4 энкодерами, 4 декодерами и скрытым размером всего 128.

2 эпохи спустя..

Validation accuracy: 0.9217000086307525

Проблема очень проста: мы пытаемся классифицировать изображение 28x28. Использование полной модели трансформеров для такой простой задачи — излишество. В представленной здесь реализации я также добавил поддержку использования архитектуры только для кодировщика. Давайте теперь попробуем использовать это для задачи классификации цифр MNIST.

2 эпохи спустя..

Validation accuracy: 0.9113000093698501

Мы почти достигаем одинакового уровня точности проверки с дополнительным декодером и без него. Как для кодировщика, так и для декодера мы используем здесь обучаемое позиционное вложение. Распространенным выбором для этого в НЛП является использование комбинации функций синуса и косинуса, которые можно легко обобщить на последовательность другой длины во время выполнения. Поскольку длина последовательности фиксирована в случае изображений, обучаемые векторы прекрасно работают.

Все визуализированные здесь веса внимания показаны для центрального пикселя изображения, что означает, насколько центральный пиксель уделяет внимание всем остальным пикселям изображения. В строках графиков показаны веса внимания для каждого кодера/декодера (в этом примере используются 4 кодера/декодера), а столбцы соответствуют каждой главе многоголового внимания (в этом примере используется 8).

Давайте также попробуем визуализировать, как выглядит изученное позиционное встраивание.

Обратите внимание, как выглядит изученное позиционное встраивание на рисунке выше. Очень интересно, что он, по-видимому, был составлен из множества сигналов различной частоты и амплитуды, так что каждое положение можно однозначно идентифицировать. Это очень похоже на то, как функция кодирования положения определяется вручную для многих существующих функций (комбинация функций синуса и косинуса различной частоты).

Я надеюсь, что это было полезно. Идите и попробуйте сами, не стесняйтесь клонировать репозиторий GitHub и попробовать. Счастливого обучения!

Гитхаб-репозиторий