Введение: Эволюция архитектур и рождение трансформеров

История развития искусственного интеллекта и обработки естественного языка (NLP) полна элегантных прорывов. До 2017 года доминирующими моделями в задачах перевода и генерации текста были рекуррентные нейронные сети (RNN) и их продвинутые модификации вроде LSTM и GRU. Однако у этих архитектур был фундаментальный изъян — они обрабатывали текст последовательно, слово за словом. Это делало невозможным эффективное распараллеливание вычислений на графических процессорах (GPU) и приводило к «забыванию» длинного контекста: градиенты либо затухали, либо взрывались при попытке связать слова, находящиеся далеко друг от друга в предложении. В декабре 2017 года исследователи из Google Brain опубликовали эпохальную статью «Attention Is All You Need». Они предложили принципиально новую архитектуру — Трансформер (Transformer). Главная магия заключалась в отказе от рекурсии в пользу механизма внимания (Attention). Авторы доказали, что для понимания контекста не нужно читать текст подряд — модель может смотреть на все слова сразу и взвешивать их важность относительно друг друга. Самое прекрасное в архитектуре трансформеров заключается в том, что каждый её элемент решает вполне конкретную инженерную или математическую проблему. Если мы последовательно зададим себе вопросы: «Как связать слова между собой?», «Как сохранить порядок слов без рекурсии?» и «Как сделать вычисления стабильными?», мы логически придем к созданию трансформера. Давайте пройдём этот путь вместе и «изобретём» эту архитектуру с нуля, опираясь исключительно на логику и здравый смысл.

Шаг 1: Проблема контекста и рождение механизма Self-Attention

Представьте, что мы проектируем модель для машинного перевода. Нам нужно перевести предложение: «Банк у реки зарос травой». Слово «банк» может означать финансовое учреждение или берег. Чтобы понять значение, модель должна сопоставить слово «банк» со словом «реки». В классических RNN связь между далёкими словами терялась. Как решить эту проблему радикально? Давайте позволим каждому слову в предложении напрямую «посмотреть» на каждое другое слово и оценить степень их близости. Это и есть идея Self-Attention (само-внимания). В линейной алгебре отношения между объектами отлично моделируются через скалярное произведение векторов. Пусть каждое слово представлено вектором эмбеддинга $x_i$. Чтобы определить, насколько сильно первое слово связано со вторым, мы можем вычислить скалярное произведение их векторов. Однако простого скалярного произведения недостаточно. В современных трансформерах этот механизм усложнили до понятий Query (запрос), Key (ключ) и Value (значение), заимствованных из систем поиска баз данных:
  • Query ($Q$) — то, что слово «ищет» в других словах.
  • Key ($K$) — то, по чему слова «откликаются» на запросы.
  • Value ($V$) — содержательная часть слова, которую мы возьмем, если запрос и ключ совпали.
Математически формула Scaled Dot-Product Attention выглядит следующим образом:
Attention(Q, K, V) = softmax( (Q * K^T) / sqrt(d_k) ) * V
Зачем здесь деление на корень из размерности ключа $\sqrt{d_k}$? Интуитивное объяснение простое: если размерность векторов велика, скалярные произведения растут по модулю. Функция `softmax` в зонах с большими значениями аргументов имеет очень маленькие градиенты (почти нулевые), что останавливает обучение. Деление на масштабный коэффициент стабилизирует градиенты.

Шаг 2: Multi-Head Attention — смотрим на мир под разными углами

Итак, мы научили модель вычислять внимание между словами. Но давайте задумаемся: у одного и того же слова в предложении может быть множество связей разного типа. Например, в предложении «Кот съел рыбу, потому что был голоден» слово «он» или глагол могут зависеть от подлежащего грамматически, а могут семантически. Обычный единый механизм внимания попытается «усреднить» эти смыслы, что снизит точность. Как поступить инженеру? Правильно, применить принцип ансамбля или многозадачности. Давайте создадим несколько независимых механизмов внимания, которые будут работать параллельно. Каждый такой поток называют «головой» (Head).
Многоголовое внимание (Multi-Head Attention) — это разделение векторов на несколько подпространств, где модель может одновременно отслеживать и синтаксические связи, и семантические, и контекстуальные зависимости.
Процесс выглядит так:
  1. Входные матрицы проекций $Q, K, V$ линейными слоями разделяются на $h$ частей (голов).
  2. Каждая пара $Q_i, K_i, V_i$ независимо друг от друга прогоняется через формулу Attention.
  3. Результаты работы всех голов конкатенируются (склеиваются) обратно.
  4. Финальный конкатенированный результат умножается на обучаемую матрицу весов $W^O$ для приведения к нужной размерности.
Благодаря этому модель не зацикливается на чём-то одном, а формирует комплексное многоуровневое представление текста.

Шаг 3: Позиционные эмбеддинги (Positional Encodings) — возвращаем порядок слов

Здесь мы сталкиваемся с главным парадоксом и критическим уязвимым местом нашего творения. Механизм Self-Attention устроен так, что ему абсолютно всё равно, в каком порядке поступают слова. Для формулы скалярного произведения набор векторов «Кот съел рыбу» и «Рыбу съел кот» будут выглядеть одинаково, если мы просто перемешаем их местами (это называется свойством инвариантности к перестановкам). Но в человеческих языках порядок слов имеет решающее значение! «Кошка поцарапала мальчика» и «Мальчик поцарапал кошку» — это кардинально разные ситуации. Как объяснить трансформеру, где первое слово, а где последнее, если у нас нет рекурсии, которая читает текст по очереди? Решение гениально своей простотой: раз модель не знает порядка, мы должны принудительно внедрить информацию о позиции в сами векторы слов. Мы можем добавить к эмбеддингу каждого слова специальный позиционный вектор (Positional Encoding). Оригинальные авторы трансформера использовали для этого тригонометрические функции с разными периодами (синусы и косинусы), чтобы модель могла легко экстраполировать позиционные зависимости на тексты большей длины, чем те, что были в обучающей выборке:
PE_(pos, 2i)   = sin(pos / 10000^(2i / d_model))
PE_(pos, 2i+1) = cos(pos / 10000^(2i / d_model))
Теперь итоговый вектор слова равен сумме его семантического эмбеддинга и позиционного вектора. Модель начинает «видеть» абсолютное и относительное расположение токенов в пространстве.

Шаг 4: Архитектурный клей — Residual Connections, Layer Norm и Feed-Forward слои

Мы собрали сердце трансформера: Multi-Head Attention, который дополнен позиционными эмбеддингами. Но любая глубокая нейросеть начинает страдать от проблем оптимизации: взрыв градиентов, деградация точности при увеличении числа слоев, зашумленность промежуточных представлений. Давайте применим проверенные временем инженерные паттерны глубокого обучения:

1. Остаточные связи (Residual Connections / Skip Connections)

Впервые триумфально примененные в ResNet, остаточные связи позволяют сигналу беспрепятственно проходить сквозь слои сети, суммируя вход слоя с его выходом ($x + \text{Layer}(x)$). Это решает проблему затухания градиента.

2. Нормализация слоев (Layer Normalization)

В отличие от BatchNorm, которая нормализует батч, LayerNorm нормализует активации по всем фичам для каждого отдельного примерам в выборке. Это стабилизирует динамику обучения трансформеров на этапе прогона токенов.

3. Позиционно-зависимые полносвязные сети (Feed-Forward Networks, FFN)

После того как механизм внимания собрал контекст со всего текста, каждому слову требуется «подумать» над полученной информацией индивидуально. Для этого используется простой многослойный персептрон (FFN), применяемый к каждому токену отдельно:
FFN(x) = max(0, x * W_1 + b_1) * W_2 + b_2
Собрав всё это вместе, мы получаем классический блок энкодера трансформера: `Input -> Positional Encoding -> [Multi-Head Attention -> Add & Norm -> Feed Forward -> Add & Norm]`.

Шаг 5: Полная картина — Encoder-Decoder структура

Теперь, когда у нас есть базовые кирпичики, мы можем построить полноценную архитектуру для задач перевода или генерации. Оригинальный трансформер состоял из двух симметричных частей:
  • Encoder (Кодировщик): Принимает на вход текст на исходном языке, последовательно пропускает его через блоки внимания и FFN, создавая глубокое контекстуальное векторное представление всего предложения.
  • Decoder (Декодер): Генерирует текст на целевом языке слово за словом. Главная особенность декодера — использование *Masked* (маскированного) Multi-Head Attention, чтобы модель во время обучения «не подглядывала» в будущее (не видела слова, которые идут после текущего генерируемого токена). Кроме того, декодер использует кросс-внимание (Cross-Attention), запрашивая информацию из выходов энкодера.
На питоне концептуальный пайплайн прямого прохода энкодера можно представить следующим образом:
import torch
import torch.nn as nn

class TransformerEncoderLayer(nn.Module):
    def __init__(self, d_model, nhead, dim_feedforward=2048, dropout=0.1):
        super().__init__()
        self.self_attn = nn.MultiheadAttention(d_model, nhead, dropout=dropout)
        self.linear1 = nn.Linear(d_model, dim_feedforward)
        self.dropout = nn.Dropout(dropout)
        self.linear2 = nn.Linear(dim_feedforward, d_model)
        
        self.norm1 = nn.LayerNorm(d_model)
        self.norm2 = nn.LayerNorm(d_model)
        self.dropout1 = nn.Dropout(dropout)
        self.dropout2 = nn.Dropout(dropout)

    def forward(self, src):
        # Self-Attention с residual связью и нормализацией
        attn_out, _ = self.self_attn(src, src, src)
        src = self.norm1(src + self.dropout1(attn_out))
        
        # Feed-Forward сеть
        ff_out = self.linear2(self.dropout(torch.relu(self.linear1(src))))
        src = self.norm2(src + self.dropout2(ff_out))
        return src
Этот небольшой фрагмент кода объединяет всё то, к чему мы пришли путем логических рассуждений.

Заключение

Изобретение трансформеров — это не столько магия случайных открытий, сколько триумф инженерной логики. Если последовательно отвечать на фундаментальные вопросы обработки последовательностей:
  1. Как связать далёкие слова? -> Применим механизм Attention.
  2. Как учесть многогранность языка? -> Сделаем внимание многоголовым (Multi-Head).
  3. Как вернуть порядок слов без рекурсии? -> Добавим позиционные эмбеддинги.
  4. Как стабилизировать обучение глубокой сети? -> Используем Residual Connections и LayerNorm.
...вы неизбежно придёте к архитектуре, которая легла в основу современных языковых моделей (LLM) вроде GPT, BERT, Claude и LLaMA. Понимание того, *почему* трансформеры устроены именно так, освобождает от необходимости заучивать их наизусть. Вы действительно могли бы изобрести их самостоятельно, просто решая ключевые проблемы масштабирования и контекста шаг за шагом.