Ответ
Энкодер BERT — это стек из L идентичных слоев (L=12 или 24). Каждый слой трансформер-энкодера обрабатывает последовательность векторов, обогащая их контекстуальной информацией со всех позиций.
Структура одного слоя энкодера:
- Multi-Head Self-Attention:
- Входные эмбеддинги
X(размерностьd_model) проецируются в три матрицы: Query (Q), Key (K), Value (V). - Self-Attention вычисляет взвешенную сумму значений V, где веса определяются совместимостью Q и K. Формула для одной "головы":
Attention(Q,K,V) = softmax(QK^T / sqrt(d_k))V. - Multi-Head означает, что это вычисление параллельно выполняется
hраз с разными проекционными матрицами, а результаты конкатенируются и проецируются обратно в размерностьd_model. Это позволяет модели фокусироваться на разных типах зависимостей.
- Входные эмбеддинги
- Добавление и нормализация (Add & Norm): К исходному входу слоя
Xприбавляется выход механизма внимания (остаточная связь), и результат нормализуется через LayerNorm. - Position-wise Feed-Forward Network (FFN): Это два линейных слоя с активацией GELU между ними. Применяется независимо к каждому токену в последовательности. Формула:
FFN(x) = GELU(xW1 + b1)W2 + b2. - Второе Add & Norm: Выход FFN снова складывается с входом этого блока и нормализуется.
Ключевые отличия энкодера BERT от декодера в оригинальном трансформере:
- Используется полное (не маскированное) Self-Attention, так как задача предобучения (MLM) требует доступа ко всему контексту.
- Отсутствует механизм Encoder-Decoder Attention, так как BERT — чисто энкодерная модель.
Упрощенная реализация на PyTorch для понимания:
import torch
import torch.nn as nn
import torch.nn.functional as F
class TransformerEncoderLayer(nn.Module):
def __init__(self, d_model=768, n_heads=12, ff_dim=3072):
super().__init__()
self.self_attn = nn.MultiheadAttention(d_model, n_heads, batch_first=True)
self.linear1 = nn.Linear(d_model, ff_dim)
self.linear2 = nn.Linear(ff_dim, d_model)
self.norm1 = nn.LayerNorm(d_model)
self.norm2 = nn.LayerNorm(d_model)
self.dropout = nn.Dropout(0.1)
def forward(self, src):
# 1. Multi-Head Self-Attention с остаточной связью
attn_output, _ = self.self_attn(src, src, src)
src = src + self.dropout(attn_output)
src = self.norm1(src)
# 2. Position-wise Feed-Forward Network с остаточной связью
ff_output = self.linear2(F.gelu(self.linear1(src)))
src = src + self.dropout(ff_output)
src = self.norm2(src)
return src
Стек таких слоев последовательно преобразует входные эмбеддинги, на каждом шаге интегрируя информацию со всей последовательности.