← Назад к вопросам

Что такое Attention?

3.0 Senior🔥 241 комментариев
#NLP и обработка текста#Глубокое обучение

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI30 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Attention механизм: полный разбор

Attention (внимание) — один из самых важных механизмов в современном глубоком обучении. Без него не было бы Transformer'ов, BERT, GPT, LLM.

Основная идея: Очень просто

Аналогия из жизни:

Когда вы читаете текст, вы не обращаете одинаковое внимание на все слова. Вы фокусируетесь на важные слова.

"Кот сидит на красивом матраце и СЛАДКО спит."

Внимание распределяется:
- "кот" — 30% (кто?)
- "сидит" — 5% (как-то не главное)
- "красивом" — 2% (описание)
- "спит" — 50% (главное действие!)
- "сладко" — 13% (как спит)

Attention механизм делает то же самое для нейронных сетей.

Три компонента Attention

Query (Q) — запрос ("что я ищу?")

"Дай мне важную информацию на позиции i"

Key (K) — ключи ("у кого это есть?")

"Вот список всех позиций с описанием"

Value (V) — значения ("вот что я тебе дам")

"Вот реальные данные с каждой позиции"

Как работает Attention Step-by-Step

Шаг 1: Вычисляем сходство (Score)

Score = Q · K^T / sqrt(d_k)

Это показывает, как сильно Query "интересует" каждый Key

Пример:

Query: "спит"
Keys: ["кот", "сидит", "матрац", "сладко"]

Score("спит", "кот") = 0.5  (кот спит - связь есть)
Score("спит", "сидит") = 0.7  (сидит + спит - близко по смыслу)
Score("спит", "матрац") = 0.1  (не связано)
Score("спит", "сладко") = 0.8  (сладко спит - очень связано)

Шаг 2: Нормализуем (Softmax)

Attention_weights = softmax(Score)

Это даёт вероятности (сумма = 1)

Пример:

Score: [0.5, 0.7, 0.1, 0.8]
↓ softmax
Attention_weights: [0.15, 0.25, 0.10, 0.50]

(Всё в сумме даёт 1.0)

Шаг 3: Взвешиваем Value'ы

Output = Attention_weights · V

Мы берём значения, но в пропорции внимания

Пример:

Attention_weights: [0.15, 0.25, 0.10, 0.50]
Values: [
  [1, 0, 0],  # "кот" embedding
  [0, 1, 0],  # "сидит" embedding
  [0, 0, 1],  # "матрац" embedding
  [1, 1, 0]   # "сладко" embedding
]

Output = 0.15*[1,0,0] + 0.25*[0,1,0] + 0.10*[0,0,1] + 0.50*[1,1,0]
       = [0.65, 0.75, 0.10]

Результат: микс информации, но больше от "сладко" (50% внимания)

Формулы Attention

Scaled Dot-Product Attention:

Attention(Q, K, V) = softmax(Q·K^T / √d_k)·V

где:
d_k = размер Key (для масштабирования)
√d_k = делим на корень, чтобы gradients не взрывались

Пример в коде (PyTorch)

import torch
import torch.nn.functional as F

# Входные данные
Q = torch.randn(batch_size=32, seq_len=10, d_model=64)  # Query
K = torch.randn(batch_size=32, seq_len=10, d_model=64)  # Key
V = torch.randn(batch_size=32, seq_len=10, d_model=64)  # Value

# Attention
def scaled_dot_product_attention(Q, K, V, mask=None):
    # Шаг 1: Score = Q · K^T
    scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(Q.size(-1))
    # Shape: (batch, seq_len, seq_len)
    
    # Маскирование (опционально, для каузальности)
    if mask is not None:
        scores = scores.masked_fill(mask == 0, float('-inf'))
    
    # Шаг 2: Attention weights = softmax(scores)
    attention_weights = F.softmax(scores, dim=-1)
    # Shape: (batch, seq_len, seq_len)
    
    # Шаг 3: Output = attention_weights · V
    output = torch.matmul(attention_weights, V)
    # Shape: (batch, seq_len, d_model)
    
    return output, attention_weights

output, weights = scaled_dot_product_attention(Q, K, V)

Multi-Head Attention

Проблема Single-Head Attention: Одна голова "внимания" может фокусироваться только на один аспект.

Решение: несколько "голов"

Вместо одного attention механизма,
мы запускаем h независимых attention'ов:

Head 1: Фокусируется на синтаксис
Head 2: Фокусируется на семантику
Head 3: Фокусируется на грамматику

Потом конкатенируем результаты
class MultiHeadAttention(nn.Module):
    def __init__(self, d_model, num_heads):
        super().__init__()
        self.num_heads = num_heads
        self.d_model = d_model
        
        assert d_model % num_heads == 0
        self.d_k = d_model // num_heads
        
        # Линейные слои для Q, K, V
        self.W_q = nn.Linear(d_model, d_model)
        self.W_k = nn.Linear(d_model, d_model)
        self.W_v = nn.Linear(d_model, d_model)
        self.W_o = nn.Linear(d_model, d_model)  # Output projection
    
    def forward(self, Q, K, V):
        batch_size = Q.shape[0]
        
        # Линейные трансформации
        Q = self.W_q(Q)  # (batch, seq_len, d_model)
        K = self.W_k(K)
        V = self.W_v(V)
        
        # Разделяем на num_heads
        Q = Q.view(batch_size, -1, self.num_heads, self.d_k).transpose(1, 2)
        K = K.view(batch_size, -1, self.num_heads, self.d_k).transpose(1, 2)
        V = V.view(batch_size, -1, self.num_heads, self.d_k).transpose(1, 2)
        # Shape: (batch, num_heads, seq_len, d_k)
        
        # Attention для каждой головы
        scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(self.d_k)
        attention = F.softmax(scores, dim=-1)
        context = torch.matmul(attention, V)
        
        # Объединяем головы обратно
        context = context.transpose(1, 2).contiguous()
        context = context.view(batch_size, -1, self.d_model)
        
        # Final linear projection
        output = self.W_o(context)
        
        return output

Зачем нужен √d_k?

Проблема без масштабирования:

# Без √d_k
scores = torch.matmul(Q, K.transpose(-2, -1))  # Большие числа!

# Пример
Q = torch.randn(1, 10, 512)  # d_k = 512
K = torch.randn(1, 10, 512)
scores = torch.matmul(Q, K.transpose(-2, -1))
# scores имеет масштаб ~512 (очень большие значения!)

# softmax(большие числа) → один элемент близко к 1, остальные к 0
# Это "резкое" внимание, градиенты убывают

С масштабированием:

d_k = 512
scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(d_k)
# scores имеет масштаб ~1

# softmax(нормальные числа) → распределение более мягкое
# Лучше градиенты для обучения

Где используется Attention

1. Transformer'ы (Self-Attention)

Каждое слово "смотрит" на все остальные слова
Q = K = V = input

2. Seq2Seq (Cross-Attention)

Декодер (Query) смотрит на энкодер (Key, Value)
Q = decoder_state
K, V = encoder_output

3. Vision Transformer'ы

Вместо слов — patches изображения
Attention помогает модели связать части картинки

4. LLM'ы (GPT, BERT, LLaMA)

Вся архитектура построена на self-attention
Каждый token смотрит на предыдущие токены (или все)

Визуальный пример Attention

СловоTEST: "The cat sat on the mat"

Когда модель обрабатывает слово "mat":

Attention distribution:
"The" [0.05]
"cat" [0.10]
"sat" [0.05]
"on"  [0.05]
"the" [0.15]
"mat" [0.60] ← максимум на себя!

Модель сфокусирована на "mat" (60%),
немного на "the" (15%),
и немного на "cat" (10%)

Плюсы и минусы Attention

Плюсы:

  • Может захватывать долгосрочные зависимости
  • Параллельная обработка (в отличие от RNN)
  • Интерпретируемо (видим, на что смотрит модель)
  • Масштабируется на длинные последовательности (относительно)

Минусы:

  • O(n²) памяти (пропорционально длине последовательности в квадрате)
  • Для очень длинных текстов может быть медленным
  • Требует много данных для обучения

Итог

Attention = селективная фокусировка на важную информацию

Формула:

Attention(Q, K, V) = softmax(Q·K^T / √d_k)·V

1. Q и K определяют, что важно
2. V содержит реальные значения
3. softmax даёт вероятности внимания
4. Результат — взвешенная сумма V

Это позволило перейти от RNN (последовательно, медленно) к Transformer'ам (параллельно, быстро, масштабируемо) и создало революцию в NLP.