Какие преимущества и недостатки у рекуррентных нейронных сетей (RNN)?

«Какие преимущества и недостатки у рекуррентных нейронных сетей (RNN)?» — вопрос из категории Нейронные сети и Deep Learning, который задают на 26% собеседований Data Scientist / ML Инженер. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Преимущества RNN:

  • Естественная работа с последовательностями: Архитектура с циклом позволяет обрабатывать данные переменной длины (предложения, временные ряды), сохраняя "память" о предыдущих элементах через скрытое состояние (hidden state).
  • Разделение весов во времени (weight sharing): Одна и та же матрица параметров применяется на каждом временном шаге, что уменьшает количество параметров по сравнению с полносвязной сетью, развёрнутой во времени.
  • Теоретическая способность улавливать долгосрочные зависимости: В идеале, скрытое состояние может нести информацию из начала последовательности.

Недостатки и практические проблемы "ванильных" RNN (SimpleRNN):

  • Проблема исчезающих/взрывающихся градиентов: При обратном распространении ошибки через много временных шагов градиенты могут экспоненциально затухать или расти, что делает обучение длинных последовательностей практически невозможным.
  • Последовательная природа вычислений: Обработка идёт шаг за шагом, что не позволяет эффективно параллелизовать вычисления на GPU, в отличие от CNN или Transformer. Это приводит к долгому обучению.
  • Слабая реальная "память": На практике SimpleRNN плохо запоминает информацию из далёкого прошлого.

Решение: Gated RNN (LSTM, GRU) Для борьбы с затуханием градиентов были разработаны архитектуры с вентирными механизмами:

  • LSTM (Long Short-Term Memory): Имеет ячейку (cell state), которая действует как "конвейерная лента", и три гейта (input, forget, output), управляющие потоком информации.
  • GRU (Gated Recurrent Unit): Упрощённая версия LSTM с двумя гейтами (update и reset).

Пример реализации LSTM для прогнозирования временных рядов на PyTorch:

import torch.nn as nn

class LSTMModel(nn.Module):
    def __init__(self, input_size=1, hidden_size=50, num_layers=2, output_size=1):
        super().__init__()
        self.hidden_size = hidden_size
        self.num_layers = num_layers
        self.lstm = nn.LSTM(input_size, hidden_size, num_layers, batch_first=True)
        self.fc = nn.Linear(hidden_size, output_size)

    def forward(self, x):
        # x shape: (batch_size, seq_length, input_size)
        h0 = torch.zeros(self.num_layers, x.size(0), self.hidden_size).to(x.device)
        c0 = torch.zeros(self.num_layers, x.size(0), self.hidden_size).to(x.device)
        out, _ = self.lstm(x, (h0, c0))  # out: (batch_size, seq_length, hidden_size)
        # Берём выход только с последнего временного шага для прогноза
        out = self.fc(out[:, -1, :])
        return out

Вывод: Хотя LSTM/GRU решили ключевые проблемы и широко применялись для NLP и временных рядов, сегодня для многих задач (особенно NLP) они вытеснены Transformer-архитектурами, которые лучше улавливают глобальные зависимости и идеально параллелятся.