Это часть 1/2 книги Dissecting BERT, написанной Мигелем Ромеро и Франсиско Ингамом. Каждая статья написана совместно обоими авторами. Если вы уже знакомы с архитектурой кодировщика из Внимание - это все, что вам нужно, и вас интересуют отличия, которые делают BERT потрясающим, перейдите к Специфика BERT.
Большое спасибо Яннет Интериан за ее обзор и отзывы.
В этом сообщении блога мы собираемся подробно изучить архитектуру Encoder (см. Рисунок 1), как описано в статье Все, что вам нужно. В BERT Specifics мы познакомимся с новыми модификациями, которые делают BERT особенно эффективным.
Обозначение
Прежде чем мы начнем, давайте определимся с обозначениями, которые мы будем использовать в статье:
emb_dim: размер встраиваемых токенов.
input_length: длина входной последовательности (одинаковая для всех последовательностей в определенном пакете из-за заполнения).
hidden_dim: размер скрытого слоя сети прямой связи.
vocab_size: количество слов в словаре (полученное из корпуса).
Вступление
Кодировщик, используемый в BERT, представляет собой архитектуру обработки естественного языка (NLP), основанную на внимании, которая была представлена в статье Все, что вам нужно, внимание год назад. В документе представлена архитектура, называемая преобразователем, которая состоит из двух частей: кодировщика и декодера. Поскольку BERT использует только кодировщик, мы собираемся объяснить это только в этом сообщении в блоге (если вы хотите узнать о декодере и о том, как он интегрирован с Encoder, мы написали об этом отдельную запись в блоге).
Трансферное обучение быстро стало стандартом для современных результатов в НЛП с момента выпуска ULMFiT в начале этого года. После этого значительные успехи были достигнуты за счет объединения Transformer с трансферным обучением. Двумя яркими примерами этой комбинации являются OpenAI GPT и Google БЕРТ AI.
Цель этой серии:
- Обеспечьте интуитивное понимание базовой архитектуры Transformer и BERT.
- Объясните основные принципы того, что делает BERT таким успешным в задачах НЛП.
Чтобы объяснить эту архитектуру, мы воспользуемся подходом от общего к конкретному. Мы начнем с рассмотрения информационного потока в архитектуре, а затем погрузимся во входные и выходные данные кодировщика, представленные в документе . Затем мы рассмотрим каждый из блоков кодировщика и поймем, как используется Multi-Head Attention. Не волнуйтесь, если вы еще не знаете, что это такое; мы убедимся, что вы это поняли к концу этой статьи.
Поток информации
Поток данных через архитектуру выглядит следующим образом:
- Модель представляет каждый токен как вектор размера emb_dim. С одним вектором внедрения для каждого из входных токенов у нас есть матрица размеров (input_length) x (emb_dim) для конкретной входной последовательности.
- Затем он добавляет позиционную информацию (позиционное кодирование). Этот шаг возвращает матрицу размеров (input_length) x (emb_dim), как и на предыдущем шаге.
- Данные проходят через N блоков кодера. После этого получаем матрицу размеров (input_length) x (emb_dim).
Примечание. Размеры входа и выхода блока кодировщика одинаковы. Следовательно, имеет смысл использовать выход одного блока кодера в качестве входа следующего блока кодера.
Примечание. В экспериментах BERT количество блоков N (или L, как они его называют) было выбрано равным 12 и 24.
Примечание. блоки не имеют одинаковых весов друг с другом.
От слов к векторам
Токенизация, оцифровка и встраивание слов
Токенизация, оцифровка и встраивание не отличаются от того, как это делается с RNN. Учитывая предложение в корпусе:
" Привет, как дела?"
Первый шаг - токенизировать его:
" Привет, как дела?" → [«Привет», «,», «как», «есть», «ты», «?»]
За этим следует числовое значение, сопоставляя каждый токен с уникальным целым числом в словаре корпуса.
[«Привет», «,«, «как», «есть» »,« ты »,«? »] → [34, 90, 15, 684, 55, 193]
Далее мы получаем вложение для каждого слова в последовательности. Каждое слово последовательности отображается в размерный вектор emb_dim, который модель будет изучать во время обучения. Вы можете думать об этом как о векторном поиске каждого токена. Элементы этих векторов рассматриваются как параметры модели и оптимизируются с обратным распространением, как и любые другие веса.
Поэтому для каждого токена ищем соответствующий вектор:
Складывая каждый из векторов вместе, мы получаем матрицу Z размеров (input_length) x (emb_dim):
Важно отметить, что заполнение использовалось для того, чтобы входные последовательности в пакете имели одинаковую длину. То есть мы увеличиваем длину некоторых последовательностей, добавляя токены «‹pad›. Последовательность после заполнения может быть такой:
[«‹Pad›», «‹pad›», «‹pad›», «Привет», «,«, как »,« есть »,« вы »,«? »] →
[5, 5, 5, 34, 90, 15, 684, 55, 193]
если для input_length было установлено значение 9.
Позиционное кодирование
Примечание. В BERT авторы использовали заученные позиционные вложения. Если вас интересует только BERT, вы можете пропустить этот раздел, в котором мы объясняем функции, используемые для расчета позиционных кодировок в Attention is All You Need.
На данный момент у нас есть матричное представление нашей последовательности. Однако эти представления не кодируют тот факт, что слова появляются в разных позициях.
Интуитивно мы стремимся изменить представленное значение конкретного слова в зависимости от его положения. Мы не хотим изменять полное представление слова, но мы хотим немного изменить его, чтобы закодировать его положение.
Подход, выбранный в статье, заключается в добавлении чисел между [-1,1] с использованием предопределенных (неизученных) синусоидальных функций к вложениям токенов. Обратите внимание, что теперь для остальной части Кодировщика слово будет представлено немного по-другому в зависимости от позиции, в которой находится слово (даже если это одно и то же слово).
Более того, мы хотели бы, чтобы кодировщик мог использовать тот факт, что некоторые слова находятся в заданной позиции, в то время как в той же последовательности другие слова находятся в других конкретных позициях. То есть мы хотим, чтобы сеть могла понимать относительные позиции, а не только абсолютные. Выбранные авторами синусидальные функции позволяют представить позиции в виде линейных комбинаций друг друга и, таким образом, позволяют сети изучать относительные отношения между позициями токенов.
Подход, выбранный в статье для добавления этой информации, заключается в добавлении к Z матрицы P с позиционным кодированием.
Z + P
Авторы решили использовать комбинацию синусоидальных функций. Математически, используя i для позиции токена в последовательности и j для позиции функции внедрения:
Более конкретно, для данного предложения P матрица позиционного вложения будет следующей:
Авторы объясняют, что результат использования этого детерминированного метода вместо изучения позиционных представлений (как мы это делали с вложениями) приводит к аналогичной производительности. Более того, этот подход имел некоторые преимущества перед изученными позиционными представлениями:
- input_length можно увеличивать до бесконечности, так как функции могут быть вычислены для любой произвольной позиции.
- Требовалось изучить меньше параметров, а модель обучить быстрее.
Полученная матрица:
X = Z + P
является входом первого блока кодировщика и имеет размеры (input_length) x (emb_dim).
Блок кодировщика
В общей сложности N блоков кодировщика объединяются в цепочку для генерации выходных данных кодировщика. Конкретный блок отвечает за поиск взаимосвязей между входными представлениями и их кодирование в своих выходных .
Интуитивно этот итеративный процесс через блоки поможет нейронной сети зафиксировать более сложные отношения между словами во входной последовательности. Вы можете думать об этом как об итеративном построении значения входной последовательности в целом.
Многоголовое внимание
Преобразователь использует Multi-Head Attention, что означает, что он вычисляет внимание h разное время с разными матрицами весов, а затем объединяет результаты вместе.
Результат каждого из этих параллельных вычислений внимания называется головой. Мы собираемся обозначить конкретный заголовок и соответствующие весовые матрицы индексом i.
Как показано на рисунке 7, после вычисления всех головок они будут объединены. В результате получится матрица размеров (input_length) x (h * d_v). После этого будет применен линейный слой с весовой матрицей W⁰ размеров (h * d_v) x (emb_dim), что приведет к окончательному результату с размерами (input_length) x (emb_dim) . Математически:
Где Q, K и V - заполнители для разных входных матриц. В частности, для этого случая Q, K и V будут заменены выходной матрицей предыдущего шага X.
Масштабируемое внимание к скалярному продукту
Обзор
Каждая голова будет характеризоваться тремя различными проекциями (матричными умножениями), заданными матрицами:
Для вычисления головы мы возьмем входную матрицу X и отдельно спроецируем ее с указанными выше матрицами весов:
Примечание: в документе d_k и d_v установлены так, что d_k = d_v = emb_dim / h
Когда у нас есть K_i, Q_i и V_i, мы используем их для вычисления масштабируемого внимания к точечному продукту:
Графически:
Примечание: в блоке кодировщика при вычислении внимания маска не используется. В нашей публикации Decoder мы объясняем, как декодер использует маскировку.
Углубляясь
Это ключ к архитектуре (название статьи не случайно), поэтому нам нужно внимательно его понять. Начнем с рассмотрения матричного произведения между Q_i и K_i транспонированным:
Помните, что Q_i и K_i были разными проекциями токенов в пространство измерений d_k. Следовательно, мы можем рассматривать скалярное произведение этих прогнозов как меру сходства между проекциями токенов. Для каждого вектора, проецируемого через Q_i, скалярное произведение с проекциями через K_i измеряет сходство между этими векторами. Если мы вызовем v_i и u_j проекции i-го токена и j-го токена через Q_i и K_i соответственно, их скалярное произведение можно увидеть как:
Таким образом, это показатель того, насколько похожи направления u_i и v_j и насколько велика их длина (чем ближе направление и чем больше длина, тем больше скалярное произведение).
Другой способ думать об этом матричном произведении - это кодирование определенной связи между каждым из токенов во входной последовательности (связь определяется матрицами K_i, Q_i ).
После этого умножения матрица поэлементно делится на квадратный корень из d_k для масштабирования.
Следующим шагом является построчное применение Softmax (одно вычисление softmax для каждой строки):
В нашем примере это может быть:
Результатом будут строки с числами от нуля до единицы, сумма которых равна единице. Наконец, результат умножается на V_i, чтобы получить результат головы.
Пример 1
Для понимания предложим фиктивный пример. Предположим, что получившаяся первая строка:
это [0,0,0,0,1,0]. Следовательно, поскольку 1 находится в 5-й позиции вектора, результат будет следующим:
Где v_ {token} - это проекция через V_i представления токена. Обратите внимание, что в этом случае слово «привет» заканчивается представлением, основанным на 4-м токене «вы» ввода для этой заголовка.
Предположим аналогичный пример для остальных голов. Слово «Привет» теперь будет представлено объединением различных проекций других слов. Со временем сеть узнает, какие отношения более полезны, и на основе этих отношений будет связывать токены друг с другом.
Пример 2
Давайте теперь немного усложним пример. Предположим теперь наш предыдущий пример в более общем сценарии, где есть не просто 1 в строке, а десятичные положительные числа, сумма которых равна 1:
Если мы сделаем, как в предыдущем примере, и умножим это на V_i:
В результате получается матрица, в которой каждая строка представляет собой композицию проекции представлений токена через V_i:
Обратите внимание на то, что мы можем рассматривать результирующее представление «Hello» как взвешенную комбинацию (центроид) проецируемых векторов через V_i входных токенов.
Таким образом, конкретная голова фиксирует определенные отношения между входными токенами. Теперь, если мы сделаем это h раз (всего h голов), каждый блок кодировщика захватит h различных отношений между входными токенами.
Далее предположим, что приведенный выше пример относится к первой голове. Тогда первая строка будет:
Тогда первая строка результата слоя Multi-Head Attention, то есть представление «Hello» в этой точке, будет
Это вектор длины emb_dim, учитывая, что матрица W_0 имеет размеры (d_v * h) x (emb_dim). Применяя ту же логику к остальным представлениям строк / токенов, мы получаем матрицу размеров (input_length) x (emb_dim).
Таким образом, на данном этапе представление токена представляет собой конкатенацию h взвешенных комбинаций представлений токенов (центроидов) через h различных изученных проекций.
Позиционная сеть прямой связи
Этот шаг состоит из следующих слоев:
Математически для каждой строки вывода предыдущего слоя:
где W_1 и W_2 - матрицы (emb_dim) x (d_F) и (d_F) x (emb_dim) соответственно .
Обратите внимание, что на этом этапе векторные представления токенов не «взаимодействуют» друг с другом. Это эквивалентно выполнению вычислений по строкам и складыванию результирующих строк в матрицу.
Выходные данные этого шага имеют размер (input_length) x (emb_dim).
Выпадение, добавление и норма
Перед этим слоем всегда есть слой, для которого входы и выходы имеют одинаковые размеры (Multi-Head Attention или Feed-Forward). Мы назовем этот слой Sublayer и его вход x.
После каждого подслоя выпадение применяется с 10% вероятностью. Назовите этот результат Dropout (Sublayer (x)). Этот результат добавляется к входу подслоя x,, и мы получаем x + Dropout (Sublayer (x)).
Обратите внимание, что в контексте слоя Multi-Head Attention это означает добавление исходного представления токена x к представлению на основе связи с другими токенами. Это как сказать токену:
«Изучите отношения с остальными токенами, но не забывайте то, что мы уже узнали о вас!»
Наконец, вычисляется токеновая / построчная нормализация со средним значением и стандартным отклонением для каждой строки. Это улучшает стабильность сети.
Результатом этих слоев является:
Вот и все! Это архитектура, лежащая в основе всей магии современного НЛП.
Если у вас есть отзыв, дайте нам знать в разделе комментариев!
использованная литература
Внимание - все, что вам нужно; Vaswani et al., 2017.
BERT: предварительная подготовка глубоких двунаправленных преобразователей для понимания языка; Девлин и др., 2018.
Аннотированный трансформатор; Александр Раш, Винсент Нгуен и Гийом Кляйн.
Настройка универсальной языковой модели для классификации текста; Ховард и др., 2018.
Улучшение понимания языка с помощью генеративного предварительного обучения; Рэдфорд и др., 2018.
Источник обложки: Cripttografia e numeri primi