RNNs/LSTMs

Visão geral

Redes Neurais Recorrentes (Recurrent Neural Networks, RNNs) são uma família de Redes Neurais projetadas para modelar sequências mantendo um estado oculto (hidden state) que é atualizado passo a passo conforme novos elementos chegam. Essa recorrência dá às RNNs um viés indutivo (inductive bias) para dados temporais ou ordenados, como texto, áudio e séries temporais.

RNNs clássicas (“vanilla”), no entanto, têm dificuldade em aprender dependências de longo alcance devido a gradientes que desaparecem/explodem (vanishing/exploding gradients) durante o treinamento. Redes de Memória de Curto e Longo Prazo (Long Short-Term Memory, LSTM) lidam com isso por meio de mecanismos de portas (gating mechanisms) e de uma célula de memória (memory cell) explícita, que melhora o fluxo de gradientes e permite aprendizado em contextos mais longos. Uma variante com portas intimamente relacionada é a GRU (Unidade Recorrente com Portas, Gated Recurrent Unit).

Embora a modelagem moderna de sequências em larga escala seja dominada pela Arquitetura Transformer, RNNs/LSTMs continuam importantes:

  • como fundamentos conceituais do aprendizado de sequências,
  • em cenários com restrições de latência ou memória,
  • para inferência em streaming/online,
  • e para certas aplicações de séries temporais e embarcadas.

Por que sequências são diferentes

Em muitos problemas de aprendizado de máquina (machine learning, ML), os exemplos são vetores de tamanho fixo. Sequências introduzem dois desafios principais:

  1. Comprimento variável: entradas (e muitas vezes saídas) podem ser mais longas ou mais curtas.
  2. Ordem e dependência: o significado de um elemento depende do contexto (por exemplo, palavras anteriores em uma frase).

RNNs lidam com isso iterando sobre passos de tempo e resumindo informações passadas em um estado.

RNNs vanilla: recorrência como atualização de estado

Dada uma sequência de entrada (x_1, x_2, \dots, x_T), uma RNN vanilla mantém um estado oculto (h_t):

[ h_t = \tanh(W_{xh} x_t + W_{hh} h_{t-1} + b_h) ] [ y_t = W_{hy} h_t + b_y ]

  • (x_t): entrada no passo de tempo (t) (frequentemente uma incorporação (embedding) para tokens)
  • (h_t): estado oculto que resume o passado
  • (y_t): saída (por exemplo, logits (logits) sobre o próximo token)
  • os parâmetros (W_{xh}, W_{hh}, W_{hy}) são compartilhados ao longo do tempo

Desenrolamento no tempo

Uma RNN pode ser vista como uma rede profunda em que a mesma camada é repetida (T) vezes (uma por passo de tempo). Essa visão “desenrolada” é crucial para entender o treinamento: os gradientes precisam se propagar por muitas transformações repetidas.

Padrões comuns de uso de RNN

  • Muitos-para-um: sequência → único rótulo (classificação de sentimento).
  • Muitos-para-muitos:
    • saídas alinhadas (rotular cada token),
    • saídas deslocadas (modelagem de linguagem: prever o próximo token),
    • saídas não alinhadas (tradução via codificador–decodificador).

Treinando RNNs: Retropropagação no Tempo (Backpropagation Through Time, BPTT)

RNNs são treinadas com Retropropagação no Tempo (Backpropagation Through Time, BPTT), uma extensão da Retropropagação que retropropaga gradientes pelo grafo computacional desenrolado.

Em princípio, a BPTT completa retropropaga por todos os (T) passos. Na prática, para sequências longas, muitos sistemas usam BPTT truncado (truncated BPTT):

  • Dividir sequências longas em blocos (por exemplo, 256 passos),
  • Retropropagar apenas dentro de cada bloco,
  • Carregar o estado oculto adiante entre blocos (opcionalmente destacando-o do grafo).

Isso reduz o uso de memória e estabiliza o treinamento, ao custo de limitar o comprimento máximo de dependência que o modelo consegue aprender.

Gradientes que desaparecem e explodem

Quando você multiplica repetidamente por Jacobianos (Jacobians) ao longo dos passos de tempo, os gradientes podem:

  • desaparecer (encolher em direção a 0): o modelo não consegue aprender dependências de longo alcance
  • explodir (crescer muito): o treinamento fica instável

Esses problemas são particularmente agudos para RNNs vanilla porque (h_t) é transformado repetidamente pela mesma matriz recorrente (W_{hh}) e comprimido por (\tanh) ou (\sigma).

Mitigações comuns:

  • Corte de gradientes (gradient clipping) (por exemplo, limitar a norma global a 1.0)
  • Inicialização cuidadosa (matrizes recorrentes ortogonais podem ajudar)
  • Uso de arquiteturas com portas (LSTM/GRU)
  • Janelas de truncamento menores (às custas de contexto longo)

LSTMs: mecanismos de portas e memória persistente

LSTMs introduzem um estado da célula (cell state) (c_t) projetado para carregar informação por longos intervalos de tempo com menor atenuação. Elas regulam o fluxo de informação com portas:

  • porta de esquecimento (forget gate): o que apagar da memória
  • porta de entrada (input gate): que nova informação escrever
  • porta de saída (output gate): o que expor como estado oculto

Uma formulação comum:

[ f_t = \sigma(W_f x_t + U_f h_{t-1} + b_f) ] [ i_t = \sigma(W_i x_t + U_i h_{t-1} + b_i) ] [ \tilde{c}t = \tanh(W_c x_t + U_c h{t-1} + b_c) ] [ c_t = f_t \odot c_{t-1} + i_t \odot \tilde{c}t ] [ o_t = \sigma(W_o x_t + U_o h{t-1} + b_o) ] [ h_t = o_t \odot \tanh(c_t) ]

Onde:

  • (\sigma) é a sigmoide,
  • (\odot) é a multiplicação elemento a elemento.

Por que LSTMs ajudam

A principal melhoria é a atualização aditiva no estado da célula: [ c_t = f_t \odot c_{t-1} + \dots ] Isso fornece um caminho para os gradientes menos propenso a desaparecer do que aplicar repetidamente uma transformação totalmente compressora. A porta de esquecimento também pode aprender a manter (c_{t-1}) quase inalterado por muitos passos.

GRUs: uma alternativa mais simples com portas

A GRU simplifica a LSTM ao mesclar o estado da célula e o estado oculto e usar duas portas:

  • porta de atualização (update gate) (z_t): quanto manter do passado
  • porta de redefinição (reset gate) (r_t): quanto do passado usar ao computar o estado candidato

Equações típicas:

[ z_t = \sigma(W_z x_t + U_z h_{t-1}) ] [ r_t = \sigma(W_r x_t + U_r h_{t-1}) ] [ \tilde{h}t = \tanh(W_h x_t + U_h (r_t \odot h{t-1})) ] [ h_t = (1 - z_t)\odot h_{t-1} + z_t \odot \tilde{h}_t ]

GRUs frequentemente têm desempenho comparável ao de LSTMs com menos parâmetros e computação ligeiramente mais rápida, embora os resultados dependam da tarefa e do ajuste.

Variantes arquiteturais e extensões

RNNs empilhadas (profundas)

Múltiplas camadas recorrentes podem ser empilhadas, onde cada camada consome os estados ocultos da camada abaixo. Isso aumenta a capacidade de representação de forma semelhante a MLPs mais profundas, mas pode ser mais difícil de otimizar.

RNNs bidirecionais (BiRNN / BiLSTM)

Uma RNN bidirecional (bidirectional) processa a sequência para frente e para trás e concatena os estados. Isso melhora o desempenho quando a sequência completa está disponível (por exemplo, rotulagem e classificação), mas não é adequado para cenários estritamente causais/em streaming.

Codificador–decodificador (sequência-para-sequência, seq2seq) com atenção

Antes dos Transformers, uma abordagem dominante para tradução e sumarização era LSTMs codificador–decodificador (encoder–decoder), frequentemente com atenção (attention):

  • O codificador lê a sequência de entrada em estados.
  • O decodificador gera a sequência de saída passo a passo.

Isso é historicamente importante e ainda é usado em cenários com restrições, mas a maior parte do seq2seq em larga escala migrou para Transformers.

Técnicas de regularização

Escolhas comuns:

  • Dropout (dropout) nas entradas e entre camadas
  • Dropout recorrente (recurrent dropout) (aplicar dropout em conexões nas computações recorrentes com cuidado)
  • Decaimento de peso (weight decay)
  • Parada antecipada (early stopping)

Algumas implementações também aplicam normalização (por exemplo, normalização de camada (layer norm)) dentro de células recorrentes, embora isso seja menos padronizado do que em Transformers.

Exemplo prático: modelo de linguagem de próximo caractere (PyTorch)

Um exemplo mínimo usando nn.LSTM para previsão do próximo passo em nível de caractere (ilustrativo, não pronto para produção):

import torch
import torch.nn as nn

class CharLSTM(nn.Module):
    def __init__(self, vocab_size, emb_dim=64, hidden_dim=256, num_layers=2):
        super().__init__()
        self.emb = nn.Embedding(vocab_size, emb_dim)
        self.lstm = nn.LSTM(
            input_size=emb_dim,
            hidden_size=hidden_dim,
            num_layers=num_layers,
            batch_first=True
        )
        self.fc = nn.Linear(hidden_dim, vocab_size)

    def forward(self, x, state=None):
        # x: (batch, time) of token IDs
        e = self.emb(x)                 # (batch, time, emb_dim)
        out, state = self.lstm(e, state) # out: (batch, time, hidden_dim)
        logits = self.fc(out)           # (batch, time, vocab_size)
        return logits, state

# Training step sketch:
# logits, _ = model(x) where x is input chars and targets are x shifted by 1
# loss = CrossEntropyLoss(logits.reshape(-1, V), targets.reshape(-1))
# loss.backward(); clip_grad_norm_(model.parameters(), 1.0); optimizer.step()

Notas práticas importantes:

  • Para modelagem de linguagem, os alvos normalmente são a entrada deslocada em um passo de tempo.
  • Use corte de gradientes para estabilizar o treinamento.
  • Para fluxos longos de texto, use BPTT truncado e carregue o estado oculto entre blocos enquanto destaca o grafo.

Exemplo prático: previsão de séries temporais com LSTMs

Para previsão univariada (por exemplo, prever o próximo valor a partir das (k) observações anteriores):

  • Entradas: janelas deslizantes (sliding windows) ([x_{t-k}, \dots, x_{t-1}])
  • Saída: (x_t)

Boas práticas comuns:

  • Normalizar/padronizar a série.
  • Incluir características exógenas (exogenous features) (características de calendário, entradas futuras conhecidas) se disponíveis.
  • Comparar com baselines simples (seasonal naive, ARIMA, modelos lineares). LSTMs podem sofrer overfitting em conjuntos de dados pequenos.

LSTMs são particularmente úteis quando:

  • as relações são não lineares,
  • múltiplos sinais correlacionados estão presentes,
  • e o conjunto de dados é grande o suficiente para sustentar o aprendizado.

Onde RNNs/LSTMs são usadas

Processamento de linguagem natural (natural language processing, NLP) (uso histórico e nichos atuais)

  • Modelagem de linguagem (era pré-Transformer)
  • Rotulagem de sequência: POS tagging, NER
  • Classificação de texto (muitos-para-um)
  • Processamento de texto em dispositivo ou em streaming

No NLP moderno, Transformers dominam devido ao paralelismo e ao melhor tratamento de contexto longo, mas RNNs ainda aparecem em pipelines de baixa latência ou em implantações com pouco espaço de memória.

Fala e áudio

  • Componentes de ASR em streaming
  • Detecção de palavra-chave
  • Modelagem acústica (historicamente proeminente)

RNNs se encaixam naturalmente em áudio em streaming porque processam quadros sequencialmente sem exigir contexto completo.

Modelagem de séries temporais

  • Manutenção preditiva
  • Fusão de sensores
  • Previsão (frequentemente com ressalvas e baselines fortes)

Aprendizado por reforço e ambientes parcialmente observáveis

Políticas com RNN podem manter memória de observações passadas para lidar com observabilidade parcial.

Pontos fortes e fracos

Pontos fortes

  • Computação causal, passo a passo: natural para streaming e inferência online.
  • Custo computacional fixo por token: processar um novo token não exige revisitar todos os tokens anteriores (em contraste com atenção sobre um contexto crescente).
  • Bom viés indutivo para dinâmicas temporais em alguns domínios.

Pontos fracos

  • Mais difícil de paralelizar durante o treinamento: a dependência sequencial entre passos de tempo reduz a utilização de GPU em comparação com Transformers.
  • Dependências de longo alcance continuam desafiadoras: LSTMs ajudam, mas não resolvem totalmente tarefas com contextos extremamente longos.
  • Complexidade de otimização: sensibilidade à inicialização, taxas de aprendizado, comprimento de truncamento e clipping.

RNNs/LSTMs vs Transformers e Modelos de Espaço de Estados

  • Transformers (Arquitetura Transformer) usam atenção para conectar diretamente tokens distantes, treinam de forma eficiente com paralelismo e escalam extremamente bem. Agora são o padrão para grandes modelos de linguagem e multimodais.
  • Modelos de Espaço de Estados (Modelos de Espaço de Estados) fornecem uma rota alternativa para eficiência em sequências longas, muitas vezes com melhores propriedades de escalabilidade do que RNNs clássicas e trade-offs diferentes dos da atenção.

Quando uma RNN/LSTM ainda faz sentido:

  • Você precisa de streaming com restrições rígidas de latência.
  • O modelo precisa ser pequeno e rodar em CPU/dispositivos de borda (edge devices).
  • Os comprimentos de sequência são moderados e você quer um baseline forte com mecanismos mais simples do que atenção.
  • Você tem razões específicas do domínio para preferir recorrência (alguns pipelines de processamento de sinais).

Dicas de implementação e treinamento (testadas na prática)

  • Use células com portas (LSTM/GRU) a menos que haja um motivo para não usar.
  • Faça clipping dos gradientes (clipping por norma global é comum).
  • Escolha o comprimento de truncamento com cuidado para BPTT truncado (troque contexto por estabilidade e memória).
  • Agrupamento em lotes (batching): agrupe sequências de comprimentos semelhantes e use preenchimento (padding) + mascaramento (masking) quando aplicável.
  • Inicialização: inicialização ortogonal para matrizes recorrentes pode ajudar; o viés da porta de esquecimento às vezes é inicializado positivamente para incentivar “lembrar” no começo do treinamento.
  • Monitoramento: acompanhe perda explodindo/NaNs; reduza a taxa de aprendizado ou aperte o clipping se estiver instável.

Conclusão conceitual

RNNs modelam sequências ao atualizar recorrentemente um estado, e LSTMs (e GRUs) tornam essa recorrência treinável em horizontes mais longos ao introduzir portas que controlam o que lembrar, escrever e expor. Mesmo em uma era dominada por Transformers, entender RNNs/LSTMs fornece uma base fundamental sobre modelagem de sequências — e elas continuam práticas em aplicações de streaming e com restrições de recursos.