В этом посте рассматриваются этапы создания и обучения упрощенной версии языковой модели преобразователя. Хотя существует бесчисленное количество ресурсов, посвященных архитектуре и реализации моделей трансформеров, этот пост предназначен для людей, которые только знакомятся с тонкостями модели. Конечно, я внимательно слежу за учебными пособиями, приведенными в официальной документации по tensorflow и keras, а также на таких страницах, как machinelearningmastery.com. Тем не менее, при реализации некоторых компонентов, таких как скалярное внимание, я руководствуюсь собственным пониманием работы модели.

На следующем рисунке показана структура модели, которую я собираюсь реализовать. Он будет использоваться для создания уникальных псевдонимов путем предоставления начального набора символов. Следовательно, в режиме обучения последовательность символов длины n будет передана в модель путем пропуска ее последнего элемента. Затем задача модели состоит в том, чтобы сопоставить входные данные с выходными, которые представляют собой исходную последовательность, сдвинутую вправо. Хэштег в конце означает конец последовательности, которая будет обозначать точку выхода для генератора последовательности, который я создаю из обученной модели.

Трансформатор на рисунке просто состоит из N блоков трансформатора оранжевого цвета. Блоки принимают на вход размерные матрицы mxd_model», где m — длина последовательности, а d_model — размер модели. Для первого блока это просто матрица вложений символов, для последующих блоков это вывод предыдущего блока. Авторегрессивная единица внимания — это точечный продукт внимания для элементов последовательности, замаскированный таким образом, что данный символ обращает внимание только на предшествующие ему. Хотя на рисунке изображен один треугольник, в этом подслое внимания есть 8 (может быть больше или меньше) головок.

На приведенном выше рисунке показано, как работает многоголовое внимание, представленное одним прямоугольным треугольником на рисунке 1. Входная матрица размером mxd_model для блока преобразователя умножается три раза на матрицы Wq, Wk и Wv, чтобы получить, соответственно, Запрос (Q), Ключ (K ) и матрицы значений (V) размеров «m x d_k» (или «m x d_k», поскольку обычно d_k = d_v). Затем эти матрицы разбиваются на 8 частей (с помощью tf.reshape()), как показано на рисунке. Скалярное произведение внимания выполняется для троек (Q_i, K_i, V_i) для i = 1,2, …, 8. Затем результаты объединяются, чтобы вернуться к размеру «m x d_k». Выход подвергается нормализации и отправляется в сеть прямой связи. Важно отметить, что на этапе нормализации размерность вывода подуровня внимания с несколькими головками (внимание_слоя_выхода) должна быть совместима с входом (x) в подуровень из-за к операции tf.LayerNormalization(x + Attention_layer_output). Поэтому размеры d_k и d_model имеют одинаковый номер. Сеть прямой связи нормализует свои выходные данные, а также возвращает матрицу размера «m x d_model». Эти операции повторяются N раз (глубина модели), прежде чем модель Transformer передаст выходные данные в окончательный плотный слой с размерностью «m x vocab_size» и активирует softmax для получения предсказания распределения вероятностей для каждой позиции в последовательности.

Подуровень внимания

В следующем фрагменте кода представлен код для многоголового внимания в модели. Все компоненты модели будут реализованы как вызываемые подклассы классов tf.keras.layer.Layer и tf.keras.Model.

dot_prod_attention усекает символы вперед с помощью future_mask и большого отрицательного числа -1e9. Я видел некоторые реализации, в которых padding_mask применялся только по столбцам, что означало бы, что отступы не будут учитываться. Однако они также не должны присутствовать, поэтому я также применяю padding_mask построчно (обратите внимание, что применение по столбцам не требуется из-за future_mask). Я применяю его к матрице весов, потому что применение к матрице оценок с padding_mask * -1e9 не избавляет от отступов (все отступы будут присутствовать с одинаковым весом 1/d_k ).

Трансформаторный блок

Ниже приведена реализация блока преобразователя, представленного в скетче 1. Обратите внимание, что поведение слоя отсева следует контролировать с помощью параметра обучения. Во время обучения он должен быть True, тогда как для генерации имени он должен быть установлен в False, потому что мы не хотим, чтобы какие-либо элементы были удалены из матричных вычислений.

Позиционное вложение и модель

Код для позиционного встраивания был получен и адаптирован из документации tensorflow.

Transformer является подклассом класса tf.keras.Model, и весь прямой проход реализован в методе call, как указано в документации. Код для создания маски заполнения, маскированной функции потерь, расчета точности и обучения модели был получен и адаптирован из серии учебных пособий Transformer Стефании Кристины на веб-странице machinelearningmastery.com.

Подготовка данных и модели для обучения

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

Я в значительной степени выполняю те же шаги при подготовке данных для обучения. Как отмечалось в начале, к концу каждого имени добавляется #, чтобы модель знала, где находится конец имени. После построения модели, запускающей ее на примере данных, появляется следующая сводка.

Генератор имен

Генератор имен будет использоваться как во время, так и после обучения. Во время обучения это позволяет увидеть, как прогностическая сила модели улучшается по мере обучения.

Экземпляр генератора, конечно, должен включать в себя обратное и обратное сопоставление индекса символов, размер словаря и максимальную длину последовательности. По завершении каждой эпохи он будет генерировать 5 имен, каждый раз продолжая выборку до тех пор, пока не будет нажато #.

Обучение

Обучение выполняется с помощью декоратора tf.function для увеличения скорости. Цикл обучения проходит через пакеты 20 раз и по завершении каждой эпохи сообщает о потерях, точности и названиях образцов.

Ниже приведен отрывок из тренировочного процесса, в котором сравниваются результаты 1-й и 20-й эпох.

Конечно, как отмечено в моем предыдущем посте, в никнеймах не так много структуры из-за отсутствия морфологических правил. Более того, они представляют собой смеси букв, цифр и символов Юникода. Следовательно, полученные имена не всегда имеют смысл.

Тем не менее, я надеюсь, что этот пост поможет устранить пробелы в понимании модели для новичков в этой области.