Что такое RNN (Recurrent Neural Network)?

Ответ

RNN (Recurrent Neural Network) — это архитектура нейронных сетей, разработанная для обработки последовательных данных, где порядок элементов имеет значение. Её ключевая особенность — наличие скрытого состояния (hidden state), которое передается от одного шага последовательности к следующему, создавая подобие "памяти" о предыдущих элементах.

Основная идея: На каждом шаге t сеть получает вход x_t и предыдущее скрытое состояние h_{t-1}, чтобы вычислить новое скрытое состояние h_t и, часто, выход y_t.

Математически (для простой RNN-ячейки): h_t = tanh(W_{xh} * x_t + W_{hh} * h_{t-1} + b_h)

Проблемы классических RNN и их решения:

  1. Проблема исчезающих/взрывающихся градиентов: при обратном распространении ошибки через много временных шагов градиенты могут стать аномально малыми или большими, что мешает обучению.
  2. Решение: использование более сложных ячеек с механизмами управления памятью:
    • LSTM (Long Short-Term Memory): имеет три "ворота" (input, forget, output) и cell state, что позволяет лучше сохранять информацию на длинных дистанциях.
    • GRU (Gated Recurrent Unit): упрощенная версия LSTM с двумя воротами, часто работает быстрее и требует меньше данных.

Пример реализации простой RNN на PyTorch для классификации тональности текста:

import torch
import torch.nn as nn

class SentimentRNN(nn.Module):
    def __init__(self, vocab_size, embed_dim, hidden_dim, output_dim):
        super().__init__()
        self.embedding = nn.Embedding(vocab_size, embed_dim)
        self.rnn = nn.RNN(embed_dim, hidden_dim, batch_first=True)
        self.fc = nn.Linear(hidden_dim, output_dim) # output_dim=1 для бинарной классификации

    def forward(self, text):
        # text shape: [batch_size, seq_length]
        embedded = self.embedding(text) # [batch_size, seq_length, embed_dim]
        output, hidden = self.rnn(embedded)
        # Берём скрытое состояние с последнего шага
        return self.fc(hidden.squeeze(0))

# Пример инициализации
model = SentimentRNN(vocab_size=10000, embed_dim=100, hidden_dim=256, output_dim=1)

Области применения: Машинный перевод, генерация текста, анализ временных рядов, распознавание речи.

Ответ 18+ 🔞

А, слушай, вот тебе про RNN, рекуррентные сети. Представь себе нейронку, которая не просто тупо смотрит на картинку, а может помнить, что было секунду назад. Ну как ты, когда сериал смотришь — если в середину заскочить, нихуя не понятно, а если с начала, то сюжет ясен. Вот RNN примерно для такого: чтобы порядок имел значение.

Основная фишка у неё — это скрытое состояние (hidden state), типа её кратковременная память, ёпта. На каждом новом шаге она жрёт новый кусок данных (x_t), но при этом не забывает, о чём только что думала (h_{t-1}). Всё это перемешивает в своей голове и выдаёт обновлённую память (h_t). Формула простая, но, бля, от неё потом все проблемы и пошли:

h_t = tanh(W_{xh} * x_t + W_{hh} * h_{t-1} + b_h)

Проблемы, блядь, классических RNN:

  1. Исчезающие/взрывающиеся градиенты — вот это пиздец, чувак. Обучаешь сеть на длинных текстах или временных рядах, а она нихуя не учится. Градиенты, которые идут назад во времени, либо в ноль уходят (исчезают), либо до небес раздуваются (взрываются). В итоге веса либо не обновляются, либо летят в космос. Доверия к такой архитектуре — ноль ебать.

  2. Решение, к счастью, нашли. Придумали ячейки поумнее, с механической памятью, прям как костыли для старого деда:

    • LSTM (Long Short-Term Memory): У этой мартышлюшки целых три воротца (input, forget, output) и отдельная "клеточная память". Она решает, что запомнить, что забыть, и что на выход пустить. Хитрая жопа, зато на длинных дистанциях работает.
    • GRU (Gated Recurrent Unit): Упрощённая версия LSTM, ворот всего два. Часто работает шустрее, особенно когда данных овердохуища нет.

Вот тебе пример кода на PyTorch, чтобы почувствовать, как это выглядит на практике. Делаем классификатор тональности:

import torch
import torch.nn as nn

class SentimentRNN(nn.Module):
    def __init__(self, vocab_size, embed_dim, hidden_dim, output_dim):
        super().__init__()
        self.embedding = nn.Embedding(vocab_size, embed_dim)
        self.rnn = nn.RNN(embed_dim, hidden_dim, batch_first=True)
        self.fc = nn.Linear(hidden_dim, output_dim) # output_dim=1 для бинарной классификации

    def forward(self, text):
        # text shape: [batch_size, seq_length]
        embedded = self.embedding(text) # [batch_size, seq_length, embed_dim]
        output, hidden = self.rnn(embedded)
        # Берём скрытое состояние с последнего шага
        return self.fc(hidden.squeeze(0))

# Пример инициализации
model = SentimentRNN(vocab_size=10000, embed_dim=100, hidden_dim=256, output_dim=1)

Где это всё впаривают? Да везде, где важен порядок: машинный перевод (чтобы "кот съел мышь" не превратилось в "мышь съела кота"), генерация текста, прогнозы по временным рядам и, ёпта, распознавание речи. Без этой штуки мы бы до сих пор с голосовыми помощниками как с хуем в пальто разговаривали.