Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Градиентное затухание (Vanishing Gradient Problem)
Градиентное затухание — это проблема глубокого обучения, при которой градиенты становятся экспоненциально малыми при обратном распространении ошибки через слои нейронной сети, что препятствует обучению на ранних слоях.
Почему это происходит
При обратном распространении (backpropagation) градиенты ошибки умножаются по цепному правилу:
∂L/∂w1 = ∂L/∂w_n · ∂w_n/∂w_{n-1} · ... · ∂w_2/∂w_1
Если каждая производная функции активации (например, сигмоида) меньше 1, то произведение экспоненциально убывает:
import numpy as np
# Демонстрация затухания градиентов
gradient = 1.0
num_layers = 50
for i in range(num_layers):
# Производная сигмоиды максимум 0.25
gradient *= 0.25
print(f"Layer {i+1}: gradient = {gradient:.2e}")
# Результат: gradient ≈ 10^-31 (практически ноль)
Проблемы, вызываемые затуханием градиентов
- Ранние слои практически не обучаются — веса не обновляются
- Долгое обучение — весь процесс замедляется
- Неустойчивое обучение — модель может застрять в локальных минимумах
- Плохие характеристики — особенно на RNN и LSTM без специальных мер
Решения
1. Использование ReLU вместо Sigmoid/Tanh:
import torch
import torch.nn as nn
# Плохо: сигмоида вызывает затухание
model_sigmoid = nn.Sequential(
nn.Linear(100, 100),
nn.Sigmoid(), # Производная max = 0.25
nn.Linear(100, 100),
nn.Sigmoid(),
nn.Linear(100, 10)
)
# Хорошо: ReLU не затухает (производная = 1 при x > 0)
model_relu = nn.Sequential(
nn.Linear(100, 100),
nn.ReLU(),
nn.Linear(100, 100),
nn.ReLU(),
nn.Linear(100, 10)
)
2. Batch Normalization:
model = nn.Sequential(
nn.Linear(100, 100),
nn.BatchNorm1d(100), # Нормализует входы слоя
nn.ReLU(),
nn.Linear(100, 100),
nn.BatchNorm1d(100),
nn.ReLU()
)
3. Skip Connections (ResNets):
class ResidualBlock(nn.Module):
def __init__(self, in_channels, out_channels):
super().__init__()
self.conv1 = nn.Conv2d(in_channels, out_channels, 3, padding=1)
self.bn1 = nn.BatchNorm2d(out_channels)
self.relu = nn.ReLU()
self.conv2 = nn.Conv2d(out_channels, out_channels, 3, padding=1)
self.bn2 = nn.BatchNorm2d(out_channels)
def forward(self, x):
residual = x
out = self.conv1(x)
out = self.bn1(out)
out = self.relu(out)
out = self.conv2(out)
out = self.bn2(out)
out += residual # Прямая связь помогает градиентам
out = self.relu(out)
return out
4. LSTM для RNN:
# Обычная RNN затухает при обратном распространении через время
rnn = nn.RNN(input_size=100, hidden_size=100, num_layers=10)
# LSTM имеет механизмы для сохранения градиентов
lstm = nn.LSTM(input_size=100, hidden_size=100, num_layers=10)
5. Правильная инициализация весов:
model = nn.Linear(100, 100)
# He инициализация для ReLU
nn.init.kaiming_uniform_(model.weight, nonlinearity="relu")
# Xavier инициализация для Sigmoid/Tanh
nn.init.xavier_uniform_(model.weight)
6. Gradient Clipping:
def train_step(model, x, y, optimizer):
optimizer.zero_grad()
output = model(x)
loss = nn.functional.mse_loss(output, y)
loss.backward()
# Ограничиваем норму градиента
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
optimizer.step()
Отличие от взрывного градиента
| Проблема | Затухание градиентов | Взрывной градиент |
|---|---|---|
| Причина | Производные < 1 | Производные > 1 |
| Симптом | Ранние слои не учатся | Нестабильное обучение, NaN |
| Решение | Batch norm, Skip connections | Gradient clipping |
Понимание и решение проблемы затухания градиентов — ключевой навык при построении глубоких нейронных сетей. Современные архитектуры (ResNets, Transformers) специально разработаны для её преодоления.