Entendendo Redes Transformer e o Mecanismo de Self-Attention

Um diagrama técnico da arquitetura Transformer mostrando as conexões de self-attention

Em 2017, o cenário da inteligência artificial mudou para sempre com a publicação do seminal artigo “Attention Is All You Need” por Vaswani et al. O artigo introduziu o Transformer, uma arquitetura de rede neural revolucionária que descartou completamente a recorrência (RNNs, LSTMs), optando por processar dados sequenciais em paralelo usando o Mecanismo de Self-Attention (Autoatendimento).

Hoje, os Transformers alimentam quase todos os modelos de linguagem de grande escala (LLMs) de ponta, incluindo GPT-4, Gemini, Claude e Llama. Este blog desmistifica a rede Transformer e explica como o mecanismo de self-attention é implementado matemática e praticamente.


1. O Gargalo do Processamento Sequencial (RNNs vs. Transformers)

Antes dos Transformers, modelos como as Redes Neurais Recorrentes (RNNs) e as redes de Long Short-Term Memory (LSTM) eram o padrão para modelagem de sequências. No entanto, as RNNs processam os tokens sequencialmente — uma palavra de cada vez. Para calcular o estado oculto da 10ª palavra, o modelo deve primeiro calcular os estados ocultos das palavras de 1 a 9.

Esta natureza sequencial introduz duas limitações graves:

  1. Sem Paralelização: GPUs modernas não podem ser utilizadas eficientemente porque as computações devem esperar que a etapa anterior seja concluída.
  2. Gradientes que Desaparecem/Explodem: As informações do início de uma sequência longa são comprimidas e perdidas no momento em que o modelo atingir o final (o problema do gargalo).

Os Transformers resolvem ambos os problemas. Ao substituir a recorrência pelo Self-Attention, o Transformer processa toda a sequência de entrada simultaneamente, permitindo uma paralelização massiva e um caminho direto entre quaisquer dois tokens em uma sequência, independentemente da distância.


2. O que é o Mecanismo de Self-Attention?

O self-attention permite ao modelo avaliar a relação entre diferentes palavras na mesma sequência. Em vez de processar uma palavra de forma isolada, o modelo representa cada palavra levando em consideração o contexto de todas as outras palavras na frase.

Por exemplo, nas frases:

  • “A manga da camisa está rasgada.”
  • “Eu adoro comer manga madura.”

A palavra “manga” tem significados diferentes dependendo do contexto. O self-attention permite ao modelo olhar para “camisa” na primeira frase e “comer” na segunda para ajustar corretamente a representação de “manga”.

A Analogia do Banco de Dados: Queries, Keys e Values

A formulação matemática do self-attention é inspirada nas buscas de recuperação de informações em bancos de dados. Para cada token de entrada, projetamos três representações vetoriais:

  1. Query ($Q$): O que o token atual está procurando.
  2. Key ($K$): A etiqueta ou perfil dos tokens na sequência.
  3. Value ($V$): O conteúdo ou informação real dos tokens.

O mecanismo de atenção computa um score de similaridade entre uma Query e todas as Keys, normaliza esses scores em pesos e retorna uma soma ponderada dos Values.


3. Passo a Passo Matemático do Scaled Dot-Product Attention

A fórmula padrão para o self-attention é chamada de Scaled Dot-Product Attention:

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

Aqui está o detalhamento matemático passo a passo de como essa fórmula é executada:

Passo 1: Computar as Matrizes de Projeção

Za uma matriz de sequência de entrada $X \in \mathbb{R}^{T \times d_{\text{model}}}$, multiplicamos por matrizes de peso aprendíveis $W_Q, W_K, W_V$ para obter as Queries ($Q$), Keys ($K$) e Values ($V$): $$Q = X W_Q, \quad K = X W_K, \quad V = X W_V$$

Passo 2: Calcular os Scores de Similaridade (Produto Escalar)

Calculamos o produto escalar da matriz Query $Q$ con a transposta da matriz Key $K^T$ para medir o alinhamento/relevância bruta entre todos os pares de tokens: $$\text{Scores} = QK^T$$ A matriz resultante tem dimensões $T \times T$, onde a entrada $(i, j)$ representa quanta atenção o token $i$ deve prestar ao token $j$.

Passo 3: Escalar os Scores

Os scores são divididos pela raiz quadrada da dimensão da chave ($d_k$): $$\text{Scaled Scores} = \frac{QK^T}{\sqrt{d_k}}$$ Por que escalar? Se $d_k$ for grande, os produtos escalares crescem muito em magnitude, empurrando a função softmax para regiões com gradientes extremamente pequenos (problema do gradiente desaparecendo). Escalar por $\sqrt{d_k}$ estabiliza o processo de treinamento.

Passo 4: Aplicar Softmax (Pesos de Atenção)

Aplicamos uma função softmax ao longo de cada linha para normalizar os scores em uma distribuição de probabilidade (valores entre 0 e 1 que somam 1): $$\text{Attention Weights} = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)$$

Passo 5: Soma Ponderada dos Values

Finalmente, multiplicamos os pesos de atenção pela matriz Value $V$: $$\text{Output} = \text{Attention Weights} \times V$$ Esta etapa agrega a informação, permitindo que a representação de saída de cada token seja fortemente influenciada pelos tokens aos quais ele “prestou atenção”.


4. Multi-Head Attention

Em vez de realizar o self-attention apenas uma vez, o Transformer usa o Multi-Head Attention (Atenção Multi-Cabeça). Ele divide os vetores Query, Key e Value em $h$ dimensões menores (cabeças), realiza a atenção em cada subespaço de forma independente e paralela, e depois concatena os resultados.

Isso é crítico porque permite ao modelo prestar atenção a diferentes tipos de relações simultaneamente. Por exemplo, uma cabeça pode focar na concordância sujeito-verbo, enquanto outra cabeça se concentra na resolução de pronomes ou referências temporais.


5. Implementação Python/NumPy do Mecanismo de Atenção

Para entender a implementação, vamos escrever uma simulação simples e autocontida em Python do Scaled Dot-Product Attention e do Multi-Head Attention usando o NumPy:

import numpy as np

def softmax(x):
    # Softmax estabilizado para evitar estouro
    exp_x = np.exp(x - np.max(x, axis=-1, keepdims=True))
    return exp_x / np.sum(exp_x, axis=-1, keepdims=True)

def scaled_dot_product_attention(Q, K, V, mask=None):
    """
    Computa o Scaled Dot-Product Attention.
    Q: [batch_size, seq_len, d_k]
    K: [batch_size, seq_len, d_k]
    V: [batch_size, seq_len, d_v]
    mask: Máscara binária opcional [batch_size, seq_len, seq_len]
    """
    d_k = Q.shape[-1]
    
    # Passo 2 & 3: Computar produto escalar e escalar
    scores = np.matmul(Q, K.swapaxes(-2, -1)) / np.sqrt(d_k)
    
    # Mascaramento opcional (ex. Mascaramento causal em Decoders)
    if mask is not None:
        scores = np.where(mask == 0, -1e9, scores)
        
    # Passo 4: Aplicar softmax para obter os pesos de atenção
    attention_weights = softmax(scores)
    
    # Passo 5: Soma ponderada dos valores
    output = np.matmul(attention_weights, V)
    
    return output, attention_weights

# --- Exemplo de Execução ---
if __name__ == "__main__":
    np.random.seed(42)
    batch_size = 1
    seq_len = 4  
    d_k = 8
    d_v = 8
    
    # Gerar vetores aleatórios de Query, Key e Value
    Q = np.random.randn(batch_size, seq_len, d_k)
    K = np.random.randn(batch_size, seq_len, d_k)
    V = np.random.randn(batch_size, seq_len, d_v)
    
    output, weights = scaled_dot_product_attention(Q, K, V)
    
    print("Matriz de Pesos de Atenção (Tamanho da Sequência x Tamanho da Sequência):")
    print(np.round(weights[0], 4))
    print("\nFormato da Saída da Atenção:", output.shape)

6. Comparação Arquitetônica

Característica RNN / LSTM Transformer
Processamento Sequencial Sim (token por token) Não (sequência paralelizada)
Complexidade Computacional $O(T)$ sequencial $O(1)$ sequencial, $O(T^2)$ operações totais
Dependências de Longo Alcance Ruim (a memória desaparece nos passos) Excelente (link direto independentemente da distância)
Paralelização Impossível ao longo do eixo temporal Paralelização nativa
Consciência Posicional Implícita (inerente ao passo sequencial) Explícita (requer Positional Encoding)

7. Componentes Adicionais do Bloco Transformer

Para fazer o self-attention funcionar em uma pilha completa, a arquitetura Transformer inclui várias camadas cruciais em cada bloco:

  1. Positional Encoding: Como os Transformers processam todos os tokens de uma vez, eles não têm senso inerente de ordem. Injetamos vetores de codificação posicional (usando ondas de seno e cosseno de diferentes frequências) diretamente nos embeddings de entrada para representar a ordem dos tokens.
  2. Residual Connections: Skip-connections ao redor de cada subcamada (Atenção e Feed-Forward) ajudam os gradientes a se propagarem através de redes muito profundas sem desaparecerem.
  3. Layer Normalization: Normaliza as ativações de cada camada, estabilizando e acelerando o treinamento.
  4. Feed-Forward Networks (FFN): Um MLP posicional aplicado a cada token de forma independente, adicionando capacidade de representation não linear.

Conclusão

A mudança do Transformer da recorrência para o self-attention paralelizado desbloqueou as leis de escala da IA moderna. Ao entender queries, keys e values, vemos como os modelos podem ligar conceitos dinamicamente e construir significado em tempo real, estabelecendo as bases para as capacidades cognitivas dos LLMs modernos.


Explore mais percepções técnicas no Blog da Ghaznix →