← Назад к вопросам

В чём разница между LSTM и GRU?

2.0 Middle🔥 191 комментариев
#Глубокое обучение

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Разница между LSTM и GRU

LSTM (Long Short-Term Memory) и GRU (Gated Recurrent Unit) — это две основные архитектуры рекуррентных нейронных сетей (RNN), разработанные для решения проблемы затухания градиентов при работе с длинными последовательностями. Обе используют механизм затвора (gates) для контроля потока информации, но различаются по сложности и производительности.

Проблема стандартного RNN

Обычный RNN имеет следующее уравнение:

h_t = tanh(W_hh * h_{t-1} + W_xh * x_t + b_h)

При обратном распространении через много временных шагов (BPTT — Backpropagation Through Time), градиент либо исчезает (vanishing gradient), либо взрывается (exploding gradient), что делает невозможным обучение на длинных последовательностях.

LSTM (Long Short-Term Memory)

LSTM — более сложная архитектура с тремя типами вентилей:

1. Вентиль забывания (Forget Gate)

Определяет, какую часть предыдущего состояния ячейки нужно забыть:

f_t = sigmoid(W_f * [h_{t-1}, x_t] + b_f)
# f_t ∈ [0, 1], где 1 = полностью сохранить, 0 = полностью забыть

2. Вентиль входа (Input Gate) и кандидат ячейки (Candidate Cell State)

Определяют, какую новую информацию добавить в состояние ячейки:

i_t = sigmoid(W_i * [h_{t-1}, x_t] + b_i)
C_tilde = tanh(W_c * [h_{t-1}, x_t] + b_c)
# i_t ∈ [0, 1], C_tilde ∈ [-1, 1]

3. Вентиль выхода (Output Gate)

Определяет, какую часть состояния ячейки выводить как скрытое состояние:

o_t = sigmoid(W_o * [h_{t-1}, x_t] + b_o)

4. Обновление состояния ячейки и скрытого состояния

C_t = f_t * C_{t-1} + i_t * C_tilde
h_t = o_t * tanh(C_t)

Практический пример LSTM:

import torch
import torch.nn as nn

class LSTMModel(nn.Module):
    def __init__(self, input_size, hidden_size, num_layers, output_size):
        super().__init__()
        self.lstm = nn.LSTM(
            input_size=input_size,
            hidden_size=hidden_size,
            num_layers=num_layers,
            batch_first=True
        )
        self.fc = nn.Linear(hidden_size, output_size)
    
    def forward(self, x):
        lstm_out, (h_n, c_n) = self.lstm(x)
        # lstm_out: (batch_size, seq_len, hidden_size)
        # h_n, c_n: (num_layers, batch_size, hidden_size)
        out = self.fc(lstm_out[:, -1, :])  # Используем последний выход
        return out

# Пример использования
model = LSTMModel(input_size=10, hidden_size=50, num_layers=2, output_size=1)
x = torch.randn(32, 20, 10)  # (batch_size, seq_len, input_size)
y = model(x)
print(y.shape)  # (32, 1)

GRU (Gated Recurrent Unit)

GRU — более простая архитектура с двумя типами вентилей:

1. Вентиль сброса (Reset Gate)

Определяет, какую часть предыдущего скрытого состояния нужно забыть:

r_t = sigmoid(W_r * [h_{t-1}, x_t] + b_r)
# r_t ∈ [0, 1]

2. Вентиль обновления (Update Gate)

Определяет, какую часть нового кандидата использовать:

z_t = sigmoid(W_z * [h_{t-1}, x_t] + b_z)
# z_t ∈ [0, 1], где 1 = полностью обновить, 0 = полностью сохранить

3. Кандидат скрытого состояния

h_tilde = tanh(W_h * [r_t * h_{t-1}, x_t] + b_h)

4. Обновление скрытого состояния

h_t = (1 - z_t) * h_{t-1} + z_t * h_tilde

Обратите внимание: GRU не имеет отдельного состояния ячейки (cell state), используя только скрытое состояние.

Практический пример GRU:

import torch
import torch.nn as nn

class GRUModel(nn.Module):
    def __init__(self, input_size, hidden_size, num_layers, output_size):
        super().__init__()
        self.gru = nn.GRU(
            input_size=input_size,
            hidden_size=hidden_size,
            num_layers=num_layers,
            batch_first=True
        )
        self.fc = nn.Linear(hidden_size, output_size)
    
    def forward(self, x):
        gru_out, h_n = self.gru(x)
        # gru_out: (batch_size, seq_len, hidden_size)
        # h_n: (num_layers, batch_size, hidden_size)
        out = self.fc(gru_out[:, -1, :])
        return out

# Пример использования
model = GRUModel(input_size=10, hidden_size=50, num_layers=2, output_size=1)
x = torch.randn(32, 20, 10)
y = model(x)
print(y.shape)  # (32, 1)

Сравнение LSTM и GRU

АспектLSTMGRU
Количество вентилей3 (забывания, входа, выхода)2 (сброса, обновления)
Состояние ячейкиЕсть (C_t и h_t)Только h_t
ПараметрыБольше (~3x от GRU)Меньше
Скорость обученияМедленнееБыстрее
ПамятьБольше требуетсяМеньше требуется
Долгосрочная памятьЛучше сохраняетХорошо, но слабее
ПроизводительностьЧасто лучше на сложных задачахХорошо для простых задач
ПереобучениеМеньше риск (больше параметров)Больше риск
Сложность кодаСложнееПроще

Математическое сравнение

LSTM имеет 4 матрицы весов для каждого временного шага:

4 * (input_size + hidden_size) * hidden_size = ~4 * total_parameters

GRU имеет 3 матрицы весов:

3 * (input_size + hidden_size) * hidden_size = ~3 * total_parameters

Когда использовать LSTM или GRU?

Используй LSTM если:

  • Работаешь с очень длинными последовательностями (100+ временных шагов)
  • Нужна максимальная точность на сложных задачах
  • Память/вычисления позволяют использовать больше параметров
  • Задача требует долгосрочной памяти (например, машинный перевод)

Используй GRU если:

  • Ограничены вычислительные ресурсы
  • Работаешь с короткими или средними последовательностями
  • Нужна скорость обучения (меньше параметров)
  • Простота реализации важна
  • Начальный прототип/эксперимент

Практический пример: предсказание временного ряда

import torch
import torch.nn as nn
from torch.utils.data import DataLoader, TensorDataset

# Сравнение LSTM и GRU на задаче предсказания временного ряда
def train_model(model, train_loader, epochs=50):
    optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
    criterion = nn.MSELoss()
    
    for epoch in range(epochs):
        total_loss = 0
        for x_batch, y_batch in train_loader:
            optimizer.zero_grad()
            predictions = model(x_batch)
            loss = criterion(predictions, y_batch)
            loss.backward()
            optimizer.step()
            total_loss += loss.item()
        
        if (epoch + 1) % 10 == 0:
            print(f"Epoch {epoch + 1}, Loss: {total_loss / len(train_loader):.4f}")

# Создание примера данных
X = torch.randn(100, 20, 1)  # 100 последовательностей, 20 временных шагов
y = torch.randn(100, 1)      # 100 целевых значений
dataset = TensorDataset(X, y)
train_loader = DataLoader(dataset, batch_size=32, shuffle=True)

# Обучение обеих моделей
lstm_model = LSTMModel(input_size=1, hidden_size=32, num_layers=1, output_size=1)
gru_model = GRUModel(input_size=1, hidden_size=32, num_layers=1, output_size=1)

print("Training LSTM...")
train_model(lstm_model)
print("\nTraining GRU...")
train_model(gru_model)

Заключение

LSTM и GRU решают одну и ту же проблему затухания градиентов, но с разными компромиссами. LSTM имеет большую выразительную способность и лучше на сложных задачах, но медленнее и требует больше памяти. GRU проще, быстрее и требует меньше данных, но может быть менее точным на очень сложных задачах. Выбор зависит от характера задачи, доступных ресурсов и требуемой точности.

В чём разница между LSTM и GRU? | PrepBro