Por que sequências exigem um tipo diferente de rede
Em muitos problemas, cada exemplo não é um vetor “parado”, mas uma sequência: palavras em uma frase, medições de um sensor ao longo do tempo, cliques de um usuário, notas musicais, eventos em logs. Nesses casos, a ordem importa e a resposta em um instante pode depender do que aconteceu antes.
Uma Rede Neural Recorrente (RNN) foi criada para lidar com esse cenário ao processar a entrada passo a passo e manter um estado oculto (memória) que carrega informação do passado para o presente.
RNN: processamento sequencial com estado oculto
Uma RNN lê a sequência x_1, x_2, ..., x_T. Em cada passo t, ela atualiza um estado oculto h_t usando o estado anterior h_{t-1} e a entrada atual x_t. Uma forma comum (RNN “simples”) é:
h_t = tanh(W_hh h_{t-1} + W_xh x_t + b_h) (estado/memória)
y_t = W_hy h_t + b_y (saída, opcional por passo)Intuição: h_t funciona como um resumo do histórico até t. Se você precisa prever algo no tempo t, a rede não olha apenas para x_t, mas para h_t, que contém pistas do que veio antes.
Tipos comuns de tarefas com RNN
- Muitos-para-um: classificar uma sequência inteira (ex.: sentimento de uma frase).
- Muitos-para-muitos: produzir uma saída por passo (ex.: rotular cada palavra, prever próximo valor em cada instante).
- Um-para-muitos: gerar uma sequência a partir de uma entrada fixa (menos comum hoje sem mecanismos adicionais).
Exemplo curto: como a saída depende do histórico
Considere uma tarefa simples de texto: prever se a palavra “banco” se refere a instituição financeira ou assento, usando o contexto anterior. Suponha a sequência:
- Ouça o áudio com a tela desligada
- Ganhe Certificado após a conclusão
- + de 5000 cursos para você explorar!
Baixar o aplicativo
x_1: “Sentei no”x_2: “banco”
Se a frase fosse “Sentei no banco”, o sentido provável é “assento”. Já em “Fui ao banco”, o sentido provável é “instituição”. Note que a decisão em t=2 depende fortemente do histórico em t=1. Em uma RNN, isso aparece assim:
- Em
t=1, a rede lê “Sentei no” e atualizah_1para refletir esse contexto. - Em
t=2, ao ler “banco”, a rede combinax_2comh_1para formarh_2e então produziry_2(classe “assento” vs “financeiro”).
O ponto central: a mesma palavra em x_2 pode levar a saídas diferentes porque h_{t-1} muda conforme o histórico.
Exemplo de série temporal: detecção de mudança com memória
Agora um exemplo numérico. Suponha que você quer detectar se houve uma “subida sustentada” em uma série de temperatura. Uma regra baseada apenas em x_t falha, porque um valor alto isolado pode ser ruído. Uma RNN pode aprender um estado que represente “tendência recente”. Assim, a saída em t depende de vários passos anteriores, comprimidos em h_t.
Treinamento em sequência: o que muda na prática
Ao treinar RNNs, o erro precisa “voltar no tempo” para ajustar como o estado foi construído em passos anteriores. Isso é feito por Backpropagation Through Time (BPTT), que consiste em “desenrolar” a RNN ao longo de T passos e aplicar o gradiente através dessa cadeia temporal.
Passo a passo prático (visão de implementação)
- Defina a janela temporal: escolha o comprimento de sequência
T(ou use sequências variáveis com padding/máscara). - Inicialize o estado: frequentemente
h_0 = 0(ou um vetor aprendido). - Forward passo a passo: para
t=1..T, computeh_te a saída (se houver). - Compute a perda: pode ser apenas no último passo (muitos-para-um) ou somada/média em todos os passos (muitos-para-muitos).
- BPTT: propague gradientes do tempo
Taté1. Em prática, usa-se muito truncated BPTT (propagar por uma janela menor) para reduzir custo e estabilizar. - Atualize parâmetros: ajuste
W_hh,W_xh,W_hye vieses.
Detalhe importante: em sequências longas, o custo cresce com T e a estabilidade do gradiente se torna um problema central.
Limitações da RNN simples: gradiente que desaparece e explode
Em sequências longas, a RNN simples sofre para manter dependências distantes. O motivo é que o gradiente, ao atravessar muitos passos temporais, envolve multiplicações repetidas por matrizes e derivadas. Isso pode levar a dois comportamentos:
- Gradiente que desaparece (vanishing): o sinal de aprendizado encolhe a ponto de a rede não conseguir ajustar parâmetros para capturar dependências longas (ex.: relacionar uma palavra no início da frase com outra no final).
- Gradiente que explode (exploding): o gradiente cresce demais, causando instabilidade e atualizações enormes.
Na prática, o gradiente que explode é frequentemente mitigado com gradient clipping (limitar a norma do gradiente). Já o gradiente que desaparece motivou arquiteturas com mecanismos explícitos de memória e controle de fluxo de informação: LSTM e GRU.
LSTM: memória com portas para controlar o que lembrar e o que esquecer
A LSTM (Long Short-Term Memory) introduz um estado de memória c_t (cell state) além do estado oculto h_t. A ideia é criar um caminho mais “linear” para a informação atravessar muitos passos, com portas que decidem o que entra, o que sai e o que é esquecido.
Equações típicas:
f_t = sigmoid(W_f [h_{t-1}, x_t] + b_f) (porta de esquecimento)
i_t = sigmoid(W_i [h_{t-1}, x_t] + b_i) (porta de entrada)
g_t = tanh( W_g [h_{t-1}, x_t] + b_g) (candidato a conteúdo)
c_t = f_t * c_{t-1} + i_t * g_t (memória)
o_t = sigmoid(W_o [h_{t-1}, x_t] + b_o) (porta de saída)
h_t = o_t * tanh(c_t) (estado oculto)Interpretação das portas:
- Esquecimento (
f_t): remove informação antiga irrelevante. - Entrada (
i_t) e candidato (g_t): decide o que escrever na memória. - Saída (
o_t): decide quanto da memória vira estado oculto (o que será “exposto” para a predição).
Esse design ajuda a preservar informação por muitos passos, reduzindo o problema de dependências longas.
GRU: uma alternativa mais simples e eficiente
A GRU (Gated Recurrent Unit) simplifica a LSTM ao combinar conceitos de memória e estado oculto em uma estrutura com menos portas e parâmetros, frequentemente com desempenho similar.
z_t = sigmoid(W_z [h_{t-1}, x_t] + b_z) (porta de atualização)
r_t = sigmoid(W_r [h_{t-1}, x_t] + b_r) (porta de reset)
h~_t = tanh(W_h [r_t * h_{t-1}, x_t] + b_h)
h_t = (1 - z_t) * h_{t-1} + z_t * h~_t- Atualização (
z_t): controla quanto do passado é mantido vs. substituído pelo novo candidato. - Reset (
r_t): controla quanto do passado influencia o candidatoh~_t.
Em cenários com restrição de computação ou dados moderados, GRUs podem ser uma escolha prática por serem mais leves.
Mini-exercício guiado: previsão do próximo valor em uma sequência curta
Suponha uma série temporal curta de consumo (normalizada): [0.10, 0.12, 0.15, 0.20]. Queremos prever o próximo valor x_5. Um modelo recorrente aprende padrões como “tendência de alta” e “taxa de crescimento recente”.
Passo a passo prático
- Monte pares (entrada, alvo): use uma janela. Ex.: entrada
[0.10, 0.12, 0.15]alvo0.20. Depois, deslize a janela em séries maiores. - Escolha a saída: para prever o próximo valor, use saída no último passo (
y_T). - Defina a célula: comece com GRU/LSTM se houver dependência além de poucos passos; RNN simples pode servir para padrões muito locais.
- Treine com BPTT truncado: por exemplo, truncar em 20–100 passos em séries longas.
- Valide com janelas futuras: em séries temporais, valide respeitando a ordem (sem embaralhar no tempo).
O que a recorrência adiciona aqui: o estado aprende um resumo do comportamento recente (subida, estabilidade, sazonalidade curta), e a previsão passa a depender do histórico, não apenas do último ponto.
Quando RNNs (incluindo LSTM/GRU) ainda são úteis
Embora arquiteturas modernas baseadas em atenção sejam muito populares, RNNs continuam úteis em vários cenários práticos:
- Streaming e baixa latência: processar um passo por vez, atualizando estado, sem precisar olhar toda a sequência de uma vez (bom para dados chegando em tempo real).
- Memória e computação limitadas: LSTM/GRU podem ser mais leves que modelos de atenção em algumas configurações, especialmente para sequências não muito longas.
- Séries temporais com dependências locais/medianas: sensores, telemetria, sinais biomédicos, onde padrões recentes são altamente informativos.
- Modelos híbridos: combinar CNN (extração local) + GRU/LSTM (dinâmica temporal) em áudio, sinais e texto curto.
Tipos de dados que se beneficiam
| Tipo de dado | Exemplos | Por que recorrência ajuda |
|---|---|---|
| Texto curto/médio | classificação de intenção, rotulagem simples | captura ordem e dependências de curto alcance |
| Séries temporais | demanda, sensores IoT, finanças (com cuidado) | modela dinâmica e padrões recentes no estado |
| Áudio/sinais | energia por frame, features espectrais | integra informação ao longo do tempo |
| Eventos discretos | logs, cliques, sequências de ações | aprende transições e contexto acumulado |
Dicas práticas de uso (RNN vs LSTM vs GRU)
- RNN simples: use para sequências curtas e padrões simples; é mais fácil e rápida, mas tende a falhar em dependências longas.
- LSTM: escolha quando a tarefa exige reter informação por mais tempo e você quer mais controle (portas separadas e estado de célula).
- GRU: bom padrão inicial quando você quer desempenho próximo ao da LSTM com menos parâmetros e treinamento mais rápido.
- Gradient clipping: quase sempre útil em modelos recorrentes para evitar explosão de gradiente.
- Truncated BPTT: essencial em sequências longas para viabilizar treinamento e reduzir instabilidade.
- Máscaras/padding: ao lidar com comprimentos variáveis, garanta que a perda e o estado não sejam contaminados por padding.