Ответ
Трансформер (Transformer) — это архитектура нейронной сети, представленная в 2017 году в работе «Attention Is All You Need». Ее ключевая инновация — полный отказ от рекуррентных (RNN) и сверточных (CNN) слоев для обработки последовательностей в пользу механизма внимания (attention), что позволило эффективно распараллеливать вычисления и лучше улавливать долгосрочные зависимости.
Основные компоненты архитектуры (на примере энкодера):
- Входные эмбеддинги: Слова преобразуются в векторы.
- Позиционное кодирование (Positional Encoding): Добавляет информацию о порядке слов, так как сам механизм внимания не учитывает последовательность.
- Слой самовнимания (Self-Attention): Позволяет каждому элементу последовательности «взвешивать» влияние всех остальных элементов. Вычисляется как взвешенная сумма значений (Value), где веса определяются совместимостью запроса (Query) с ключом (Key). [ text{Attention}(Q, K, V) = text{softmax}(frac{QK^T}{sqrt{d_k}})V ]
- Многоголовое внимание (Multi-Head Attention): Несколько независимых механизмов самовнимания работают параллельно, что позволяет модели фокусироваться на разных типах взаимосвязей (например, синтаксических и семантических).
- Позиционно-зависимая полносвязная сеть (Position-wise Feed-Forward Network): Применяется независимо к каждому позиционному вектору после слоя внимания.
- Остаточные связи (Residual Connections) и нормализация слоя (LayerNorm): Стабилизируют обучение глубокой сети.
Упрощенная реализация самовнимания на PyTorch:
import torch
import torch.nn as nn
import torch.nn.functional as F
class SelfAttention(nn.Module):
def __init__(self, embed_size, heads):
super(SelfAttention, self).__init__()
self.embed_size = embed_size
self.heads = heads
self.head_dim = embed_size // heads
assert self.head_dim * heads == embed_size, "Embed size must be divisible by heads"
self.values = nn.Linear(self.head_dim, self.head_dim, bias=False)
self.keys = nn.Linear(self.head_dim, self.head_dim, bias=False)
self.queries = nn.Linear(self.head_dim, self.head_dim, bias=False)
self.fc_out = nn.Linear(heads * self.head_dim, embed_size)
def forward(self, values, keys, query, mask=None):
N = query.shape[0]
value_len, key_len, query_len = values.shape[1], keys.shape[1], query.shape[1]
# Разделение эмбеддингов на головы
values = values.reshape(N, value_len, self.heads, self.head_dim)
keys = keys.reshape(N, key_len, self.heads, self.head_dim)
queries = query.reshape(N, query_len, self.heads, self.head_dim)
energy = torch.einsum("nqhd,nkhd->nhqk", [queries, keys]) / (self.head_dim ** 0.5)
if mask is not None:
energy = energy.masked_fill(mask == 0, float("-1e20"))
attention = torch.softmax(energy, dim=3)
out = torch.einsum("nhql,nlhd->nqhd", [attention, values])
out = out.reshape(N, query_len, self.heads * self.head_dim)
return self.fc_out(out)
Применение:
- NLP: Является основой для BERT (двунаправленный энкодер), GPT (авторегрессионный декодер), T5 и других SOTA-моделей.
- Компьютерное зрение: Vision Transformer (ViT) разбивает изображение на патчи и обрабатывает их как последовательность.
- Мультимодальные задачи: Модели типа CLIP, DALL-E.
Преимущества: Высокая параллелизуемость, эффективное моделирование глобальных зависимостей. Сложности: Квадратичная вычислительная сложность относительно длины последовательности, большие требования к памяти.
Ответ 18+ 🔞
Давай разберемся, что это за зверь такой — трансформер. Ну, не тот, что в кино, а тот, что в нейросетях. Представь, что в 2017 году какие-то умные ребята взяли и сказали: «А пошли вы все нахуй со своими RNN и CNN для текстов!». И выкинули эту статью «Attention Is All You Need». И там, блядь, всё держится на механизме внимания. Это как если бы ты читал предложение и вместо того, чтобы тупо по словам ползти, мог сразу взглядом выцепить, что вот это «он» относится к тому «Васе» в начале абзаца. Удобно, да? И главное — это всё можно на видеокартах параллелить, овердохуища быстрее получается.
Из чего эта штука собрана, если брать энкодер (кодировщик):
- Входные эмбеддинги. Словам дают цифровые паспорта — векторы. Без этого нихуя не работает.
- Позиционное кодирование. А вот это хитрая жопа. Сам механизм внимания не понимает, какое слово за каким идёт. Ему похуй. Поэтому к этим векторам прикручивают специальные метки о порядке, как будто номера в гардеробе вешаешь.
- Слой самовнимания. Вот тут самое мясо, ёпта. Каждый элемент (слово) может «посмотреть» на всех остальных и решить, на кого ему обратить больше внимания. Формула там, конечно,
softmax(QK^T / sqrt(d_k)) * V, но если по-простому: запрос (Query) ищет, с какими ключами (Key) он совпадает, а потом берет оттуда значения (Value). Получается взвешенная сумма. - Многоголовое внимание. Это чтобы не быть тупым однобоким мудаком. Вместо одного такого механизма их запускают несколько параллельно. Одна «голова» может смотреть на грамматические связи, другая — на смысловые. Потом результаты склеивают. Гениально и просто.
- Полносвязная сеть для каждой позиции. После внимания каждый вектор прогоняют через свою маленькую нейросеть. Независимо от соседей.
- Остаточные связи и нормализация. Без этого глубокую сеть не обучить — всё развалится. Это как подпорки, которые не дают градиентам сдохнуть на полпути.
Вот тебе кусок кода на PyTorch, как это самовнимание выглядит под капотом. Не пугайся:
import torch
import torch.nn as nn
import torch.nn.functional as F
class SelfAttention(nn.Module):
def __init__(self, embed_size, heads):
super(SelfAttention, self).__init__()
self.embed_size = embed_size
self.heads = heads
self.head_dim = embed_size // heads
assert self.head_dim * heads == embed_size, "Embed size must be divisible by heads"
self.values = nn.Linear(self.head_dim, self.head_dim, bias=False)
self.keys = nn.Linear(self.head_dim, self.head_dim, bias=False)
self.queries = nn.Linear(self.head_dim, self.head_dim, bias=False)
self.fc_out = nn.Linear(heads * self.head_dim, embed_size)
def forward(self, values, keys, query, mask=None):
N = query.shape[0]
value_len, key_len, query_len = values.shape[1], keys.shape[1], query.shape[1]
# Разделение эмбеддингов на головы
values = values.reshape(N, value_len, self.heads, self.head_dim)
keys = keys.reshape(N, key_len, self.heads, self.head_dim)
queries = query.reshape(N, query_len, self.heads, self.head_dim)
energy = torch.einsum("nqhd,nkhd->nhqk", [queries, keys]) / (self.head_dim ** 0.5)
if mask is not None:
energy = energy.masked_fill(mask == 0, float("-1e20"))
attention = torch.softmax(energy, dim=3)
out = torch.einsum("nhql,nlhd->nqhd", [attention, values])
out = out.reshape(N, query_len, self.heads * self.head_dim)
return self.fc_out(out)
И где эту хуйню применяют? Да везде, блядь:
- Обработка текста (NLP): Это основа всех нынешних монстров. BERT — это типа энкодер, который смотрит на текст сразу целиком. GPT — это декодер, который генерирует текст слово за словом, как сумасшедший рассказчик. Все они — детища трансформера.
- Компьютерное зрение: Да-да, и картинки тоже. Vision Transformer (ViT) режет изображение на кусочки-патчи и кормит их трансформеру как последовательность. И он, уёбок, справляется, иногда даже лучше классических CNN.
- Мультимодальные задачи: Когда нужно и текст, и картинки понимать вместе. CLIP, DALL-E — все эти ваши нейрохудожники оттуда.
Плюсы: Распараллеливается на ура, видит длинные зависимости в тексте (не забывает, о чём речь шла в первом абзаце). Минусы: Сложность квадратичная от длины последовательности. Если текст длинный как хуй с горы — памяти и вычислений нужно дофига. Но игра, ядрёна вошь, стоит свеч.