Понимание Transformer Encoder в OpenNMT-tf

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

Обсуждаются такие ключевые процессы, как embedding, dropout, нормализация и механизм внимания, с упором на роли различных компонентов, таких как SelfAttentionEncoderLayer и LayerNorm. В статье также рассматриваются линейные преобразования и использование относительного позиционного кодирования, объясняется, как входные данные преобразуются через несколько слоев перед отправкой в ​​декодер. В целом, данная статья обеспечивает полное понимание функциональности кодировщика и его важности в моделях машинного обучения.

Понимание Transformer Encoder

Начало работы кодировщика

После инициализации выполняются следующие шаги:

  • набор данных финализируется, т. е. активируется весь конвейер подготовки обучающих данных;

├── dataset = self._finalize_dataset() модуль training.py

  • цикл запускается с количеством шагов, указанным в параметре Train steps ;
  • на каждом шаге цикла обучения из обучающего набора данных извлекаются группы, сгруппированные в пакеты;
  • количество групп будет равно эффективному размеру пакета // размер пакета. Например, если задать параметр эффективный размер пакета = 200 000, а размер пакета = 6 250, то количество групп будет равно 200 000 // 6 250 = 32.
  • в каждой из 32 групп будет 6250 токенов, поэтому общий объем токенов, который будет обработан за один шаг обучения, будет равен эффективному размеру пакета или батча, т. е. 200 000 токенов.
  • из группы извлекается из партий, и для каждого токена в партиях из матрицы встраивания извлекается векторное представление токена с помощью функции tf.nn.embedding_lookup.

├── call() | class WordEmbedder(TextInputter) модуль text_inputter.py

На примере нашей модели с vocab_size = 26 и num_units = 4, схематически для исходного языка это можно представить следующим образом: (Изображение 1 - матрица встраивания)

Понимание Transformer Encoder

Предположим, что наши партии состоят из 3 обучающих элементов.

Источник: (Изображение 2 - источник)

Понимание Transformer Encoder

Цель: (Изображение 3 - цель)

Понимание Transformer Encoder

В КОДИРОВАНИИ ВЕКТОРА ПОДГОТОВКИ ТОКЕНОВ исходный ЯЗЫК:

  • умножается на квадратный корень размерности - входы = входы * √numunits например num_units = 4 ; (Изображение 4 - num_units = 4)
Понимание Transformer Encoder
  • применяется dropout (параметр dropout из файла конфигурации), т.е. значения случайным образом заменяются на ноль с помощью функции tf.nn.dropout. При этом все остальные значения (кроме замененных на ноль) корректируются путем умножения на 1/(1 - p), где p - вероятность dropout. Это делается для приведения значений к одному масштабу, что позволяет использовать одну и ту же сеть для обучения (при вероятности < 1,0) и вывода (при вероятности = 1,0); (Изображение 5 - параметр dropout)
Понимание Transformer Encoder
  • По размерности партий функция sequence_mask">tf.sequence_mask строит тензор маски; для нашего примера размерность партий будет [3 3 3] и функция возвращает маску; (Изображение 6 - sequence_mask">tf.sequence_mask)
Понимание Transformer Encoder
  • В цикле для каждого слоя, равного числу параметров Layers, векторное представление пакетированного вектора (которое хранится в переменных inputs ) и тензор маски передаются в слой, а затем результат, возвращаемый слоем, передается в следующий слой. Например, если у нас 6 слоев, результат из первого слоя будет входным результатом для второго слоя и т. д.
Понимание Transformer Encoder
  • Каждый слой представляет собой объект класса SelfAttentionEncoderLayer, в котором механизм внимания реализован с помощью класса MultiHeadAttention. Механизм преобразований, происходящих в каждом слое SelfAttentionEncoderLayer, будет описан ниже.

Нормализационный слой, класс LayerNorm()

При параметре Pre norm = True к пакету после операции исключения и маскирования, перед расчетом весов внимания, применяется слой нормализации — для каждого пакета со значениями k мы вычисляем среднее значение и дисперсию:

  • mean_i = sum(x_i[j] for j in range(k))/k
  • var_i = sum((x_i[j] - mean_i) ** 2 for j in range(k))/k

Затем вычисляется нормализованное значение x_i_normalized, включая небольшой фактор эпсилон (0,001) для числовой устойчивости:

  • x_i_normalized = (x_i — mean_i) / sqrt(var_i + epsilon)

И, наконец, x_i_normalized линейно преобразуется с использованием гаммы и беты, которые являются обучаемыми параметрами (при инициализации гамма = [1,..., 1], бета = [0,..., 0]):

  • output_i = x_i_normalized * gamma + beta

Значения вычисляются матрицы запросов - нормализованные значения матрицы входов, полученные на предыдущем шаге, пропускаются через линейное преобразование слоя tf.keras.layers.Dense.

Линейное преобразование, класс Dense()

━ 1) вычисляется размерность партий → shape = [3, 3, 4] ;

━ 2) изменяет размерность tf.reshape (inputs, [-1, shape[-1]]) → tf.reshape(inputs, [-1, 4]); (Изображение 7 - tf.reshape)

Понимание Transformer Encoder

━ 3) при mixed_precision и num_units, делящихся на 8, вычисляется размер отступа и его формирование

  • padding_size = 8 - num_units % 8 → padding_size = 8 - 4 % 8 = 4
  • paddings = [[0, 0], [0, padding_size]] → paddings = [[0, 0], [0, 4]]

веса слоя ядра (которые были сформированы при инициализации) матрицы запросов дополняются отступом tf.pad(kernel, paddings); (Изображение 8 - tf.pad)

Понимание Transformer Encoder

━ 4) матрицы kernel и batched матрица (входы) tf.matmul(inputs, kernel) умножаются; (Изображение 9 - tf.matmul)

Понимание Transformer Encoder

━ 5) из полученной матрицы берется срез размерности слоя num_units и формируется матрица выходов; (Изображение 10 - outputs матрица)

Понимание Transformer Encoder

━ 6) вектор смещения добавляется к outputs матрице, полученной на предыдущем шаге (начальные значения смещения инициализируются слоем ядра и изначально равны нулю) tf.nn.bias_add(outputs, bias); (Изображение 11 - tf.nn.bias_add)

Понимание Transformer Encoder

━ 7) после добавления смещения outputs матрица подвергается линейной активации слоя activation(outputs). Линейная активация слоя - это применение функции к матрице. Поскольку функция для слоя tf.keras.layers.Dense не определена, по умолчанию функция активации равна a(x) = x, т. е. матрица остается неизменной; (Изображение 12 - activation)

Понимание Transformer Encoder

━ 8) после активации линейного слоя матрица выходов преобразуется tf.reshape(outputs, shape[:-1] + [num_units]) → tf.reshape(outputs, [3, 3, 4]). После этого шага мы получаем queries матрицу. (Изображение 13 - матрица queries)

Понимание Transformer Encoder

Полученная на предыдущем шаге матрица queries делится на количество голов (в нашей архитектуре количество голов равно 2, механизм деления описан в первой части статьи). (Изображение 14 - матрица запросов делится на количество голов)

Понимание Transformer Encoder

Делящаяся на количество голов матрица queries делится на квадратный корень: (Изображение 15 - матрица запросов делится на квадратный корень)

Понимание Transformer Encoder
  • num_units_per_head:
  • num_units_per_head = num_units // num_heads
  • num_units_per_head = 4 // 2 = 2

Согласно шагам 1-8, описанным выше (пункт Линейное преобразование), вычисляется матрица ключей и делится на количество целей. (Изображение 16 - keys матрица)

Понимание Transformer Encoder

Следуя шагам 1-8 выше, вычисляется матрица значений и делится на количество целей. (Изображение 17 - values матрица)

Понимание Transformer Encoder

Относительное кодирование

Так как в рассматриваемом примере используется относительное позиционное кодирование ( maximum_relative_position = 8 ), то следующим шагом будет относительное кодирование:

━ 1) вычисляется размерность keys матрицы:

  • keys_length = tf.shape(keys)[2]
  • keys_length = [3 2 3 2 2][2]
  • keys_length = 3;

━ 2) формируется массив целочисленных элементов длины keys_length:

  • arange = tf.range(length)
  • arange = [0 1 2];

━ 3) формируются две матрицы на оси 0 и 1 с помощью функции tf.expand_dims(input, axis) и из полученных матриц вычисляется расстояние до диагонали distance = tf.expand_dims(arange, 0) - tf.expand_dims(arange, 1); (Изображение 18 - distance матрица)

Понимание Transformer Encoder

━ 4) матрица расстояний до диагонали расстояний отсекается по значению maximum_relative_position tf.clip_by_value(distance, -maximum_position, maximum_position); (Изображение 19 - maximum_relative_position)

Понимание Transformer Encoder

━ 5) значение maximum_relative_position добавляется к матрице расстояний, полученной на предыдущем шаге; (Изображение 20 - матрица distance + maximum_position )

Понимание Transformer Encoder

━ 6) матрица relation_pos, полученная на предыдущем шаге, используется для извлечения из матрицы relation_position_keys, сформированной при инициализации модели, соответствующих элементов по индексам с помощью функции tf.nn.embedding_lookup. Затем формируется матрица relation_repr_keys ; (Изображение 21 - матрица relation_repr_keys)

Понимание Transformer Encoder

━ 7) матрица relation_repr_values ​​→ embedding_lookup (relative_position_values, relation_pos) формируется таким же образом; (Изображение 22 - матрица relation_repr_values)

Понимание Transformer Encoder

Скалярное произведение

Следующий шаг — скалярное произведение queries и keys матриц → dot = tf.matmul(queries, keys, transpose_b =True). (Изображение 23 — скалярное произведение матриц запросов и ключей)

Понимание Transformer Encoder

Матрица queries умножается на матрицу relation_repr_keys → matmul_with_relative_representations (queries, relation_repr_keys, transpose_b =True). (Изображение 24 — матрица запросов умножается на матрицу relation_repr_keys)

Понимание Transformer Encoder

К dot матрице добавляется матрица, полученная на шаге matmul_with_relative_representations. (Изображение 25 — точечная матрица)

Понимание Transformer Encoder

Dot матрица преобразуется с использованием mask матрицы, полученной из партий токенов выше:

━ 1) размерность mask матрицы изменяется

mask = tf.expand_dims(mask, 1)

━ 2) dot матрица преобразуется следующим образом

dot = (dot * mask) + (1.0 - mask) * dot.dtype.min

(Изображение 26 - матрицы точек и масок)

Понимание Transformer Encoder

Функция активации softmax применяется к dot матрице, и мы получаем матрицу attn → attn = tf.nn.softmax(dot). Функция softmax используется для преобразования вектора значений в распределение вероятностей, сумма которых равна 1. (Изображение 27 - применение softmax)

Понимание Transformer Encoder

Dropout применяется к матрице attn (параметр awareness_dropout из файла конфигурации). (Изображение 28 - применение dropout)

Понимание Transformer Encoder

После dropout матрица drop_attn умножается на матрицу values ​​для формирования матрицы heads. (Изображение 29 - матрица heads)

Понимание Transformer Encoder

Матрица drop_attn умножается на матрицу relation_repr_values. (Изображение 30 - матрица drop_attn )

Понимание Transformer Encoder

Матрица, полученная на шаге matmul_with_relative_representations, добавляется к матрице heads. (Изображение 31 - матрица heads)

Понимание Transformer Encoder

Матрица heads преобразуется в размерность исходной партии с помощью функции combine_heads - т. е. выполняются обратные операции split_heads - получается матрица combined. (Изображение 32 - матрица combined)

Понимание Transformer Encoder

Матрица combined подвергается линейному преобразованию, после чего мы получаем матрицу outputs → outputs = linear_output (combined). Линейное преобразование полностью идентично с 1-го по 8-й шаг, описанный в классе Dense(). (Изображение 33 - линейное преобразование объединенной матрицы)

Понимание Transformer Encoder

К outputs матрице применяется dropout (параметр dropout из файла конфигурации). (Изображение 34 - применение dropout к выходным матрицам)

Понимание Transformer Encoder

Применяется механизм остаточных связей ( residual connection) - матрица inputs добавляется к матрице outputs. Residual Connection - это механизм, используемый для решения проблемы исчезающего градиента в глубоких нейронных сетях и для улучшения обучения и сходимости модели. (Изображение 35 - outputs + inputs матрицы)

Понимание Transformer Encoder

Матрица выходов передается в сеть прямого распространения ( Feed Forward Network ):

━ 1) применяется слой нормализации LayerNorm ();

━ 2) линейное преобразование класса Dense();

━ 3) Линейное преобразование с функцией активации ReLU tf.nn.relu

━ 4) применяется dropout (параметр ffn_dropout из файла конфигурации);

━ 5) линейное преобразование класса Dense();

━ 6) применяется dropout (параметр dropout из файла конфигурации);

━ 7) применяется механизм остаточной связи ( Residual connection).

(Изображение 36 - передача матрицы выходов в сеть прямого распространения)

Понимание Transformer Encoder

Если значение Layers больше единицы, то после преобразования outputs матрицы с помощью сети прямого распространения полученная матрица отправляется на вход следующего слоя, пока не пройдет все слои.

Полученная матрица выходов с последнего слоя проходит слой нормализации LayerNorm () - заключительную операцию в кодере. Полученная на этом этапе матрица передается в декодер. (Изображение 37 - слой нормализации LayerNorm ())

Понимание Transformer Encoder

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

Понимание Transformer Encoder

Кодер. Упрощенная последовательность вызова:

├──def call() | class Model(tf.keras.layers.Layer) модуль model.py

├──def call() | class SequenceToSequence(model.SequenceGenerator) модуль sequence_to_sequence.py

├──def call() | class WordEmbedder(TextInputter) модуль text_inputter.py

├──def call() | class SelfAttentionEncoder(Encoder) модуль self_attention_encoder.py

├──def build_mask() | class Encoder(tf.keras.layers.Layer) модуль encoder.py

├──def call() | class LayerWrapper(tf.keras.layers.Layer) модуль layers /common.py

├──def call() | class SelfAttentionEncoderLayer(tf.keras.layers.Layer) модуль layers/transformer.py

├──def call() | class MultiHeadAttention(tf.keras.layers.Layer)модуль layers/transformer.py

├──def call() | class Dense(tf.keras.layers.Dense) модуль layers /common.py

├──def call() | class LayerWrapper(tf.keras.layers.Layer) модуль layers /common.py

├──def call() | class FeedForwardNetwork(tf.keras.layers.Layer) модуль layers/transformer.py

├──def call() | class LayerNorm(tf.keras.layers.LayerNormalization) модуль layers/common.py

Заключение

В статье представлен всесторонний обзор функции кодировщика в нейронной сети, подробно описаны различные этапы обработки входных данных. В ней описывается, как завершается набор данных и как происходит обучение в пакетах, подчеркивая важность таких механизмов, как embedding, dropout и нормализация. На протяжении всего процесса подчеркивается использование многоголового внимания, демонстрируя, как queries, keys, and values матрицы преобразуются и объединяются. В статье также объясняется применение относительного позиционного кодирования и вычислений скалярного произведения, необходимых для механизмов внимания.

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


Вас ждет еще больше увлекательного чтения

Сравнение качества систем распознавания речи

Сравнение качества систем распознавания речи

April 30, 2025

Машинный перевод в военной сфере

Машинный перевод в военной сфере

April 16, 2025

Преобразование текста в речь для колл-центров

Преобразование текста в речь для колл-центров

January 8, 2025

×