Transformer Architecture: Análisis Comparativo de Encoder y Decoder Stacks

Desglose técnico de la arquitectura Transformer (Vaswani et al., 2017) y su bifurcación en modelos Encoder-only (BERT) y Decoder-only (GPT). Análisis del mecanismo Scaled Dot-Product Attention, implementación de máscaras de causalidad y evaluación de complejidad computacional.

La publicación de Attention Is All You Need (2017) marcó un punto de inflexión al demostrar que las redes recurrentes (RNN, LSTM, GRU) no son estrictamente necesarias para el procesamiento de secuencias. La arquitectura Transformer basa su funcionamiento enteramente en mecanismos de atención, permitiendo una paralelización masiva durante el entrenamiento, a diferencia de la naturaleza secuencial de las RNN. La relevancia de este cambio arquitectónico es tal que este artículo constituye el segundo post dedicado al tema, dando continuidad al análisis previamente iniciado sobre los fundamentos y motivaciones del uso de atención en modelos Seq2Seq.

La arquitectura ha divergido en dos ramas principales para transfer learning:

  1. Decoder-only (Generativo): Ejemplificado por OpenAI GPT (junio 2018). Utiliza un stack de decoders con masked self-attention para modelado de lenguaje autoregresivo (predecir el siguiente token).
  2. Encoder-only (Representacional): Ejemplificado por BERT (Google, octubre 2018). Utiliza un stack de encoders con atención bidireccional completa para generar representaciones profundas de contexto, optimizado mediante Masked Language Modeling (MLM).

Fundamentos matemáticos

El núcleo del Transformer es el mecanismo de atención, que mapea una consulta ($Q$), claves ($K$) y valores ($V$) a una salida. La función de atención escalar, denominada Scaled Dot-Product Attention, se define como:

$$\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V$$

Donde:

  • $Q, K, V \in \mathbb{R}^{n \times d_k}$: Matrices de embeddings o estados ocultos.
  • $d_k$: Dimensión de las claves (usado como factor de escala para evitar gradientes pequeños en la softmax).
  • $n$: Longitud de la secuencia.

Para capturar diferentes subespacios de representación, se utiliza Multi-Head Attention:

$$\text{MultiHead}(Q, K, V) = \text{Concat}(\text{head}_1, ..., \text{head}_h)W^O$$$$\text{head}_i = \text{Attention}(QW_i^Q, KW_i^K, VW_i^V)$$

El siguiente diagrama ilustra el flujo de operaciones del Scaled Dot-Product Attention:

Diferencia estructural en la máscara:

En la arquitectura GPT (Decoder), se aplica una máscara de causalidad $M$ antes de la softmax para impedir que el modelo "vea" tokens futuros.

$$M_{ij} = \begin{cases} 0 & \text{si } i \ge j \\ -\infty & \text{si } i < j \end{cases}$$

En BERT (Encoder), $M_{ij} = 0$ para todo token válido, permitiendo atención bidireccional completa (cada token atiende a todos los demás).

La diferencia fundamental entre ambas arquitecturas se visualiza en sus matrices de atención:


Implementación práctica

A continuación, una implementación en PyTorch (v0.4.1) del bloque de atención, ilustrando cómo el parámetro mask define si el bloque se comporta como un Encoder (BERT) o un Decoder autoregresivo (GPT).

import torch
import torch.nn as nn
import torch.nn.functional as F
import math

class ScaledDotProductAttention(nn.Module):
    def __init__(self, d_k, dropout=0.1):
        super().__init__()
        self.d_k = d_k
        self.dropout = nn.Dropout(dropout)

    def forward(self, q, k, v, mask=None):
        # q, k, v dimensiones: [batch_size, num_heads, seq_len, d_k]
        
        # 1. Matmul y escalado
        scores = torch.matmul(q, k.transpose(-2, -1)) / math.sqrt(self.d_k)
        
        # 2. Aplicación de máscara (Crucial para diferencia GPT vs BERT)
        if mask is not None:
            # mask debe contener 0 para posiciones válidas y -1e9 para ocultas
            scores = scores.masked_fill(mask == 0, -1e9)
        
        # 3. Softmax y Dropout
        attn = F.softmax(scores, dim=-1)
        attn = self.dropout(attn)
        
        # 4. Agregación de valores
        output = torch.matmul(attn, v)
        return output, attn

# Ejemplo de máscaras
seq_len = 5

# Máscara para BERT (Encoder): Todo visible (excepto padding si lo hubiera)
bert_mask = torch.ones(seq_len, seq_len) 

# Máscara para GPT (Decoder): Triangular superior oculta (Causalidad)
gpt_mask = torch.tril(torch.ones(seq_len, seq_len))

print("Matriz de visibilidad GPT (1=visible, 0=oculto):")
print(gpt_mask)

En un modelo completo, este bloque se repite $N$ veces (12 en BERT-Base y GPT), seguido de capas Feed-Forward y normalización (LayerNorm).


Análisis de comportamiento

  1. Complejidad Computacional:
    El coste de una capa de atención es $O(n^2 \cdot d)$, donde $n$ es la longitud de la secuencia y $d$ la dimensión del modelo. Esto contrasta con las RNNs, cuyo coste es $O(n \cdot d^2)$.
  • Consecuencia: El Transformer es más rápido para secuencias donde $n < d$, pero el consumo de memoria crece cuadráticamente con la longitud de la secuencia.
  1. Paralelización:
  • Encoder (BERT): Totalmente paralelizable durante entrenamiento e inferencia (procesa toda la frase a la vez).
  • Decoder (GPT): Paralelizable durante entrenamiento (teacher forcing), pero secuencial durante inferencia (generación token a token).
  1. Captura de dependencias:

La distancia máxima del camino (path length) entre dos posiciones cualesquiera es $O(1)$. En una LSTM, la distancia es $O(n)$. Esto permite al Transformer modelar dependencias de largo alcance sin la degradación de gradiente típica de las recurrentes.


Comparativas o referencias técnicas

Comparación de arquitecturas base disponibles en el estado del arte actual (Oct 2018):

Característica Transformer (Original) OpenAI GPT BERT (Devlin et al.)
Componentes Encoder + Decoder Decoder Stack Encoder Stack
Direccionalidad Encoder Bidir. / Decoder Unidir. Unidireccional (Left-to-Right) Bidireccional
Objetivo Seq2Seq (Traducción) Language Modeling (Next Token) Masked LM & Next Sentence
Pre-entrenamiento Supervisado (WMT) No supervisado (BooksCorpus) No supervisado (Wiki + Books)
Fine-tuning Específico por tarea Task-agnostic (input transformations) Task-agnostic (output layers)

Observación sobre GLUE Benchmark:
BERT-Base ha reportado una puntuación media de 79.6% en GLUE, superando significativamente a GPT (72.8%), lo que valida la hipótesis de que la bidireccionalidad es crítica para tareas de comprensión (NLI, Q&A), aunque impide la generación directa de texto.


Limitaciones y casos donde no conviene usarlo

  1. Longitud de secuencia fija:
    La arquitectura actual impone un límite rígido (típicamente 512 tokens en las implementaciones de Google y OpenAI) debido a la matriz de Positional Encoding y al coste cuadrático de la atención. Para documentos largos, se requiere truncamiento o segmentación, perdiendo contexto global.
  2. Coste de Inferencia (Decoder):
    En tareas de generación, el Decoder debe ejecutarse $n$ veces para generar $n$ tokens. No es más rápido que una RNN durante la fase de generación.
  3. Data Hunger:
    A diferencia de modelos con sesgos inductivos fuertes (como CNNs para visión), el Transformer requiere conjuntos de datos masivos para generalizar correctamente. Entrenar un Transformer desde cero con pocos datos suele resultar en un overfitting rápido y un rendimiento inferior a una LSTM con regularización.
  4. Ausencia de recurrencia:
    Al no tener estado oculto persistente entre batches, el modelo no tiene memoria más allá de la ventana de contexto definida ($n$).