Перейти к основному содержимому

Трансформер

Трансформер (transformer) - это нейросетевая модель, призванная более эффективно решать задачи many-to-many (seq2seq) по преобразованию одной последовательности в другую. Последовательностями могут выступать

  • текст как последовательность слов,

  • видео как последовательности кадров,

  • звук как последовательность частот звуковых волн.

Примерами таких задач могут быть

  • система ответов на вопросы (вопрос-входная последовательность, ответ-выходная)

  • распознавание речи (входная последовательность - звук в виде спектрограммы, выходная - распознанная речь в виде текста)

  • прогнозирование временных рядов (входная последовательность-начало ряда, выходная - его продолжение)

  • музыкальная композиция (входная последовательность-текст песни, выходная-последовательность ноты).

В частности, модель ChatGPT расшифровывается как Chat Generative Pretrained Transformer и построена на базе трансформерной архитектуры.

За пределами many-to-many

Отдельные элементы трансформера, такие как блок самовнимания (self-attention), используются и в других постановках, таких как one-to-one (классификация, генерация изображений), one-to-many (описание текстом, что показано на изображении), many-to-one (классификация текста), что позволяет говорить о других видах трансформера.

Например, использование самовнимания на изображениях называется визуальным трансформером (visual transformer). Поскольку трансформер работает над последовательностями, изображения в нём разбиваются на последовательность фрагментов, после чего идёт работа с каждым фрагментом как с элементом "последовательности".

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

Текст можно обрабатывать не только как последовательность слов, но и как последовательность других элементарных объектов, называемых токенами, таких как отдельные символы или n-граммы символов либо слов.

В демонстрационной many-to-many задачи будем рассматривать задачу машинного перевода (machine translation), состоящую в автоматическом переводе текста с одного языка на другой. Тем более, именно для этой задачи трансформер и был впервые предложен.

Ранее мы уже изучили, что для лучшей запоминаемости входной последовательности лучше использовать не стандартную many-to-many архитектуру, а дополненную механизмом внимания (attention). Но там на этапе генерации всё ещё используется обученная рекуррентая сеть, которой приходится запоминать весь уже сгенерированный контекст в скрытом состоянии. Поскольку он представляется вектором фиксированного размера, то это всё ещё приводит к потере информации о ранее сгенерированном контексте. Эту проблему можно решить повторным применением механизма внимания к уже сгенерированным токенам, что и предложено в модели трансформера (transformer) [1], в котором полностью отказались от рекуррентности в кодировщике и декодировщике, заменим работу с историей блоками внимания.

Собственно, поэтому статья [1] и называется - Attention is all you need, и является одной из самых цитируемых работ глубокого обучения, поскольку она принципиально изменила подход к обработке последовательностей.

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

Поскольку трансформер основан исключительно на легко параллелизуемом механизме внимания безо всякого использования рекуррентности, это позволяет быстрее его обучать на видеокарте с достаточным объёмом памяти.

Архитектура трансформера

Схема модели трансформера приведена ниже [1]:

Трансформер состоит из 2-х блоков - кодировщика (encoder), расположенного на схеме слева, и декодировщика (decoder), расположенного справа. Кодировщик и декодировщик выделены прозрачными прямоугольниками.

Применение трансформера

Обработка последовательностей, как всегда в нейросетях, происходит минибатчами. TT - максимальная длина входной последовательности в минибатче последовательностей длин T1,T2,...TBT_1,T_2,...T_B:

T=max{T1,T2,...TB}.T=\max\{T_1,T_2,...T_B\}.

Поскольку последовательности будут иметь разную длину, то каждая из них дополняется специальным токеном [EOS] (end of sequence), означающим конец последовательности, а более короткие последовательности после [EOS] дополняются спец. токеном [PAD], чтобы все они в итоге оказались одной длины для параллельной обработки.

Рассмотрим пример машинного перевода с русского на английский. Минибатч, состоящий из 3-х предложений на русском

  • [Город располагается на берегу реки]

  • [В центре находится парк]

  • [Популярное место]

будет представлен как

  • [Город располагается на берегу реки EOS]

  • [В центре находится парк EOS PAD]

  • [Популярное место EOS PAD PAD PAD]

Кодировщик

На вход кодировщику поступает входная последовательность из TT токенов (слов), каждый из которых кодируется DD-мерным эмбеддингом (input embedding), обучаемым вместе с настройкой самой сети (хотя при желании их можно инициализировать и готовыми эмбеддингами).

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

Но информация о расположениях токенов внутри последовательности теряется. Чтобы модели в явном виде сообщить, какие токены где располагались, к эмбеддингу каждого токена на входе в декодировщик прибавляется эмбеддинг той же размерности, кодирующий абсолютное расположение эмбеддинга Это называется позиционным кодированием (positional encoding).

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

Например, для 2-х последовательностей:

  • замок имеет шесть башен и окружён глубоким рвом

  • замок заржавел, и ключ в нём не поворачивается

эмбеддинг слова "замок" будет разным исходя из контекста!

Блоки кодировщика

Кодировщик состоит из NN последовательно применяемых блоков кодировщика, каждый - со своими параметрами. В [1] бралось N=6N=6.

Декодировщик

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

В режиме обучения - это слова корректного перевода (режим teacher forcing), а в режиме применения - это слова, которые трансформер сам сгенерировал ранее.

Обучение декодировщика

Во время обучения декодировщику подается на вход выходная последовательность (корректный перевод на английском), сдвинутая на единицу специальным токеном [BOS] (beginning of sequence) и законченная токеном [EOS] (end of sequence) c дополнением PAD для выравнивания по максимальной длине выходной последовательности в минибатче.

Например, обучающий минибатч ответов

  • [The city is located on the river bank]

  • [There is a park in the center]

  • [A popular place]

будет закодирован как

  • [BOS The city is located on the river bank EOS]

  • [BOS There is a park in the center EOS PAD]

  • [BOS A popular place EOS PAD PAD PAD PAD PAD]

для выравнивания длин всех последовательностей.

На выходе кодировщик выдаёт эмбеддинг для каждого токена перевода, к каждому из которых в отдельности применяется линейный слой (Linear на схеме) и SoftMax преобразование, выдающее вероятностное распределение на возможных словах перевода, по которым можно предсказать уже само слово, выбрав самое вероятное.

Таким образом, по входу

[BOS A popular place EOS PAD PAD PAD PAD PAD]

ожидается выход

[A popular place EOS X X X X X X]

где по позициям X потери уже не считаются, поскольку всё, что следует после токена [EOS], не будет участвовать в выходе сети.

Параллелизация

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

Применение декодировщика

Во время применения кодировщик всё так же может обрабатывать входную последовательность параллельно, поскольку она известна целиком.

А вот выходная последовательность заранее неизвестна, поэтому декодировщик запускается много раз в авторегрессионном режиме (генерируя слова перевода последовательно одно за другим).

Например, при переводе [Популярное место EOS]:

  1. подаётся [BOS], он генерирует [A].

  2. подаётся [BOS A], он генерирует [A popular].

  3. подаётся [BOS A popular], он генерирует [A popular place].

  4. подаётся [BOS A popular place], он генерирует [A popular place EOS].

После получения токена [EOS] генерация останавливается.

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

Блоки декодировщика

Декодировщик состоит из NN последовательно применяемых блоков декодировщика, каждый - со своими параметрами. В [1] бралось N=6N=6.


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

Литература

  1. Vaswani A. Attention is all you need //Advances in Neural Information Processing Systems. – 2017.
  2. https://medium.com/analytics-vidhya/attention-is-all-you-need-demystifying-the-transformer-revolution-in-nlp-68a2a5fbd95b