Простое руководство по созданию интуиции, лежащей в основе архитектуры Трансформеров, которая изменила сферу НЛП.
Один из самых значительных прорывов в НЛП произошел всего пару лет назад, когда Ашиш Васвани и его команда представили архитектуру Transformers в 2017 году. Простота и эффективность представленной архитектуры позволили другим исследователям создать очень большие и очень впечатляющие языковые модели, такие как BERT и GPT.
Конечно, есть много источников, которые пытаются объяснить, как работают трансформаторы, к сожалению, некоторые интуитивные аспекты объяснения часто упускаются и не представлены. Таким образом, непонятно: как люди пришли к этой идее? Что было вдохновением? Что такое матрицы ключей, запросов и значений в контексте преобразователей? Как мы можем интерпретировать все компоненты?
Конечно, реализация Transformers в некоторых библиотеках, таких как Huggingface, не требует, чтобы мы знали, как работает модель, и с ней можно было бы обращаться как с черным ящиком, но, как и во многих других вещах, может быть очень полезно понять все основные механизмы, чтобы иметь либо: создать что-то новое, улучшить предыдущее решение или понять, какая задача подходит для какой операции.
В этой статье я хотел бы объяснить архитектуру трансформаторов на очень простом и интуитивно понятном уровне, чтобы даже читатель, который никогда не слышал о трансформаторах, мог проследить и понять каждую деталь. Я также считаю, что люди, знакомые с этой архитектурой, получат новый взгляд на трансформаторы, прочитав эту статью. Итак, без лишних слов, приступим.
Аналогия временных рядов.
Как мы все знаем, язык имеет последовательный характер, и порядок слов имеет значение. Подобно языку, временные ряды также имеют важность порядка, в котором появляются точки данных.Итак, давайте взглянем на один пример анализа временных рядов и проверим, можем ли мы применить аналогичные идеи к задаче НЛП. Допустим, у нас есть данные временного ряда, как показано ниже:
В этом примере у нас есть цена акции, и ее стоимость меняется с течением времени, мы хотели бы проанализировать тренд, к сожалению, наши данные зашумлены, и поэтому выделить тренд немного сложно.
В статистике известно, что мы могли бы применить операцию свертки с использованием некоторого ядра, чтобы уменьшить шум. Это можно сделать, как показано ниже для некоторой точки x_i.
После свертки мы получим шумоподавленное представление y_i нашей начальной точки данных x_i. Если мы применим свертку к нашему примеру графа, используя ядро [0,25, 0,5, 0,25], мы получим.
Как вы можете ясно видеть, общая тенденция состоит в том, что цена акций растет.
Хорошо, звучит здорово, можем ли мы применить аналогичную идею в области НЛП? Фактически, да, мы можем это сделать, и сверточные нейронные сети использовали этот подход при изучении значений для матрицы ядра, но это огромный недостаток!
В примере временных рядов мы рассмотрели, что соседние точки коррелированы друг с другом, поэтому мы можем сгладить график, к сожалению, то же самое не относится к языку (по крайней мере, не всегда). Рассмотрим пример ниже:
Когда мы с Беном играли в футбол, Джон позвонил мне и попросил помочь ему на выходных собрать его новую мебель.
В этом случае, если мы не используем большой размер окна, система не сможет понять, к кому относятся слова: «его / его». В некоторых языках, например, в немецком, может случиться так, что у вас будет очень длинное предложение, и глагол будет в конце, поэтому иногда вы будете только догадываться, что происходит в предложении, пока не прочитаете глагол в конце.
Следовательно, хотя идея перехода в пространство представления очень хороша, использование подхода свертка / окно / ядро для этого не применимо для задач НЛП.Итак, давайте подумаем, есть ли какой-нибудь другой подход, который мы могли бы использовать, и сосредоточимся на идее так называемого самовнимания.
Самовнимание.
При самовнимании главная цель - понять, как слова в предложении взаимосвязаны друг с другом. В примере предложения, который мы рассматривали ранее, если бы мы задали вопрос «Что делать?» тогда слово Бен следует связать (используя терминологию "обслуживай") с фразой "играть в футбол". точно так же слова его / его связаны со словом Джон, так как именно он просил о помощи, и его мебель необходимо собрать. Как мы видели, оконный подход здесь неприменим, поэтому давайте взглянем на другой подход.
Размышляя с точки зрения вложений НЛП.
В одной из своих предыдущих статей я рассказывал об очень крутом и полезном способе представления слов под названием «Вложения». Одно из хороших свойств вложения слов заключается в том, что если два слова похожи, их косинусное сходство будет равно 1. Для простоты предположим, что у нас есть предложение из 4 слов (следовательно, мы работаем с 4 токенами). Давайте сначала преобразуем наши токены в векторное представление, используя некоторое вложение, чтобы мы получили токены x_i.
Теперь, для простоты примера, давайте преобразуем вектор x_2 в пространство представления y_2, используя все наши векторы-слова x_1, x_2, x_3, x_4.
Точно так же, как в примере с временным рядом, мы сделаем, как показано ниже:
Где мы знаем, что такое значения x_i, и нам нужно выяснить, как вычислить веса w_ {ij}, поскольку мы не хотим использовать какие-либо жестко запрограммированные значения и хотим убедиться, что веса могут отражать сходство слов с учетом контекста предложения . Одним из возможных способов сделать это может быть скалярное произведение между двумя векторами (если два вектора являются независимыми, скалярное произведение будет равно нулю, если они похожи, скалярное произведение будет большим положительным числом). Итак, давайте сделаем это так и определим вес w_ {ij} как:
И, поскольку в этом примере мы работаем с вектором x_2, я обновлю нашу принципиальную схему, как показано ниже.
Теперь некоторые веса окажутся сверхбольшими, некоторые - относительно небольшими, поэтому, чтобы наша модель была согласованной, мы можем нормализовать веса, чтобы убедиться, что они в сумме равны 1.
И теперь, после нормализации, именно эти веса w_ {i2} будут использоваться для вычисления вектора представления y_2.
Итоговая диаграмма будет выглядеть так:
Теперь мы нашли y_2, запросив x_2, и можем применить аналогичную процедуру для поиска векторов представления для других токенов: y_1, y_3, y_4. И для согласования графика с терминологией Transformers в этом случае: Запрос, Ключевые значения отмечены, как на рисунке ниже.
Как вы могли заметить, это очень надежный и простой в исполнении метод, который даже не включал никаких нейронных сетей и параметров обучения (хотя я использовал веса слов, чтобы провести аналогию с примером временного ряда, они не связаны с весами NN).
Но ... разве мы не немного наивны, просто используя эту простую архитектуру для отражения собственного внимания? Вероятно, да, поскольку в этом случае нам все еще может быть сложно сказать, относится ли слово «он» к Бобу или к Джону. Поэтому в этой архитектуре мы полагаемся только на качество нашего встраивания, и если оно плохое, то и наши результаты будут такими же.
Итак, что можно было сделать в этом случае? Как насчет того, чтобы мы представили простую нейронную сеть прямого распространения в точках наших частей Key, Query и Value. Таким образом, наша оригинальная архитектура может быть немного обновлена следующим образом:
Именно это и происходит на самом деле за так называемым «масштабируемым вниманием к точечному продукту» Transformer. Что на бумаге показано ниже.
Где выше, Q - это матрица, содержащая запросы, K - ключи и V - значения. Что мы получаем, передавая вложения слов из нашего предложения через соответствующий канал прямой связи.
Единственные вещи, которые пока не обсуждались, - это масштабирующая часть и маска. Часть масштабирования нужна только для стабилизации градиентов. Маска используется в части декодирования во время процесса обучения, чтобы мы не видели слова, которые еще не должны видеть (если вы все еще не понимаете этого, не волнуйтесь, пока это не так важно).
И в чем прелесть этого, так это то, что все вычисления могут быть полностью выполнены параллельным образом, поскольку мы можем передать все предложение модели, которая будет выполнять все вычисления одновременно!
На самом деле эту сложную схему можно легко упростить и представить в виде формулы:
Можно задать еще один вопрос: достаточно ли нам одного внимания? К сожалению, нет, поэтому нам нужны множественные механизмы внимания.
Многоголовое внимание.
Рассмотрим следующее предложение:
Сегодня мой друг Джон купил машину.
Из этого предложения давайте посмотрим, какие слова могут быть связаны / связаны со словом «купил»:
Как вы можете видеть, в языке у нас обычно есть разные слова, связанные со словом запроса, в зависимости от «вопроса», который мы можем задать. Наличие только одного самовнимания перенасыщает наш механизм, и нам может стать слишком сложно найти, какое слово имеет наибольшее значение.
Чтобы провести аналогию со сверточными нейронными сетями, у нас есть несколько ядер на каждом уровне, где каждое из ядер изучает свои собственные уникальные особенности.
Итак, вместо использования одного самовнимания, давайте воспользуемся несколькими из них, и этот комбинированный блок называется многоголовым вниманием.
А теперь давайте посмотрим, как это работает, поскольку в нем также есть некоторые важные детали, которые нужно понять.
Как вы можете видеть на изображении выше, у нас есть предложение X (которое представляет собой матрицу, содержащую вложенные друг в друга слова x_1, x_2,…). Мы проходим через линейный уровень и получаем матрицы ключей, запросов и значений, которые затем передаем в Scaled Dot-Product Attention, как описано.Единственная разница теперь заключается в том, что нам нужно выполнить все эти операции h раз, где h соответствует количеству голов, которое мы определяем.
Теперь есть одна важная часть, на которой я хотел бы сосредоточить наше внимание.
Каждая голова не обрабатывает весь вектор внедрения, она обрабатывает только часть вектора.
Позвольте мне проиллюстрировать:
Предположим, что наше вложение имеет размер d и что у нас есть h головок, это означает, что первая головка будет обрабатывать первые измерения d / h вектора, вторая голова будет обрабатывать следующие измерения d / h,и продолжаем по той же схеме
Зачем это делается? Как мы знаем из статьи о вложениях, каждое измерение пространства встраивания может представлять некоторую специальную информацию (если у представленного слова есть крылья или тип птицы) или некоторую абстрактную информацию, например:
- к какой части речи принадлежит слово
- что представляет собой гиперплоскость между двумя осями и т. д.
Таким образом, также возможно, что встраивание может научиться отражать различный тип информации, которая поможет нам различать связь между разными словами в предложении. И при таком подходе, например, один из руководителей сможет ответить на вопрос «кто?», Другой руководитель ответит на вопрос «что сделал?», Какой-то другой руководитель узнает связь с вопросом «когда?» и т.п.
После того, как разделение вложений будет выполнено и все слова пройдут через уровень внимания Scaled Dot-Product Attention, мы объединим выходные векторы из всех наших голов, чтобы убедиться, что для каждого слова в нашем предложении мы можем ответить на конкретный вопрос ( что голова успела узнать).
В конце мы делаем еще один проход через слой Linear FF, чтобы убедиться, что наш вывод имеет определенное измерение.
В этом и заключается внимание Multi-Head, которое на самом деле является главной, самой сложной и самой запутанной частью трансформатора, который оказался не слишком сложным!
Архитектура кодировщика.
Теперь давайте посмотрим на общую картину, сосредоточимся на архитектуре кодировщика преобразователя (в этой статье я не собираюсь касаться декодера, так как он имеет аналогичную структуру с некоторыми небольшими отличиями, которые в этой статье можно опустить).
В качестве первого шага наш исходный текст проходит через Input Embedding, который является прямой частью представления вектора слова. Затем нам нужно суммировать это с помощью позиционного кодирования, которое имеет решающее значение для преобразователя, поскольку мы обрабатываем все токены одновременно, поэтому это позволяет преобразователю знать порядок токенов внутри предложения.
Мы можем либо обучить его с нуля, либо использовать детерминированный подход с помощью специальной формулы
После этого мы просто переносим наши векторы в блок Transformer, который на рисунке показан серым прямоугольником. Этот блок Transformer повторяется N раз, чтобы позволить нам иметь более глубокую архитектуру, поэтому выход блока 1 используется как вход для блока 2.
Как вы можете видеть, когда входной сигнал попадает в блок трансформатора, он проходит через механизм многоголового внимания (который мы теперь очень хорошо понимаем), но вы замечаете, что существует остаточное соединение, которое также передает наш входной вектор вокруг Multi- Блок внимания с заголовком. Почему это там?
Остаточные соединения.
Задумайтесь на секунду о тренировочном процессе. Представьте, что мы сейчас находимся в процессе обновления весов NN, и поступает сигнал градиента.
Основная проблема здесь заключается в том, что, поскольку у нас есть N блоков трансформатора, h блоков Multi-Headed Attention в каждом блоке трансформатора, мы рискуем получить проблему исчезающего градиента, что может привести к проблеме отсутствия обучения нашей NN вообще.
Таким образом, мы можем рассматривать эти остаточные связи как очень важный подход к оптимизации, а также как уловку, которая помогает нам избежать проблемы исчезающих градиентов.
Заключительные замечания.
Итак, как мы увидели в этой статье, архитектура Transformer - это очень изящное и красивое решение, которое позволило добиться невероятного прогресса в НЛП. Мне действительно нравится думать о Transformers как о ненаправленной архитектуре, поскольку мы можем напрямую передать ей все предложение, а NN сможет использовать свой механизм Self-Attention для поиска зависимостей между словами в предложении независимо от того, насколько далеко эти слова расположены в предложении.
LSTM и Bi-LSTM могут с трудом уловить эти словесные отношения в предложении из-за последовательного характера их обучения.
Трансформаторы намного проще и быстрее обучать, так как пакетные операции с этой архитектурой очень естественны. В то время как с RNN для выполнения некоторых аналогичных операций потребовалось бы реализовать некоторые специальные приемы, которые могут быть довольно сложными, а иногда и нелогичными.
Я надеюсь, что после прочтения этой статьи вам удалось понять, что Transformer - это довольно простая архитектура, если вы понимаете часть Self-Attention, и что бояться нечего!
Следите за новостями, чтобы увидеть больше статей о НЛП и машинном обучении, оставайтесь в безопасности и наслаждайтесь отдыхом!