Как работает механизм backpropagation?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Как работает механизм backpropagation?
Backpropagation (обратное распространение ошибки) — это алгоритм, который позволяет обучать нейронные сети, эффективно вычисляя градиенты функции потерь по всем параметрам.
Суть идеи
Backpropagation работает в два шага:
- Forward pass (прямой проход): данные идут через сеть, вычисляется предсказание и функция потерь
- Backward pass (обратный проход): градиенты вычисляются в обратном направлении от выхода к входу, используя цепное правило (Chain Rule)
Математическая основа: Цепное правило
Если y = f(g(x)), то:
dy/dx = (dy/dg) * (dg/dx)
Это правило позволяет разложить сложную функцию и вычислить производные.
Пример: простая нейронная сеть
Архитектура:
x (входные данные)
↓
z1 = x*W1 + b1 (скрытый слой)
↓
a1 = ReLU(z1) (активация)
↓
z2 = a1*W2 + b2 (выходной слой)
↓
Loss = MSE(z2, y)
Forward Pass:
def forward(X):
z1 = np.dot(X, W1) + b1
a1 = np.maximum(0, z1) # ReLU
z2 = np.dot(a1, W2) + b2
return z2, a1, z1
Backward Pass (Backpropagation):
def backward(X, y, output, a1, z1):
m = X.shape[0]
# Шаг 1: Градиент по выходу
dz2 = (output - y) * 2 / m
# Шаг 2: Градиенты для W2 и b2
dW2 = np.dot(a1.T, dz2)
db2 = np.sum(dz2, axis=0, keepdims=True)
# Шаг 3: Градиент по скрытому слою
da1 = np.dot(dz2, W2.T)
# Шаг 4: ReLU backward
dz1 = da1 * (z1 > 0)
# Шаг 5: Градиенты для W1 и b1
dW1 = np.dot(X.T, dz1)
db1 = np.sum(dz1, axis=0, keepdims=True)
# Обновляем параметры (Gradient Descent)
W1 -= learning_rate * dW1
b1 -= learning_rate * db1
W2 -= learning_rate * dW2
b2 -= learning_rate * db2
Вычислительный граф
x
/ \
/ \
W1 b1
\ /
z1 = x*W1 + b1
|
ReLU
|
a1
/ \
/ \
W2 b2
\ /
z2 = a1*W2 + b2
|
Loss(z2, y)
Backpropagation идёт в обратном направлении:
Loss → z2 → a1 → z1 → W1, b1
↓
W2, b2
Пошаговый пример
Forward:
x = [2, 3]
z1 = [2, 3] * W1 + b1 = [1.3, 0.8]
a1 = ReLU([1.3, 0.8]) = [1.3, 0.8]
z2 = [1.3, 0.8] * W2 + b2 = [1.06]
Loss = (1.06 - 5)^2 = 15.68
Backward:
dL/dz2 = 2 * (1.06 - 5) = -7.88
dW2 = a1.T * dL/dz2 = [1.3, 0.8] * (-7.88)
dz1 = dL/dz2 * W2.T * (z1 > 0)
dW1 = X.T * dz1
Backpropagation в PyTorch (автоматический)
import torch
import torch.nn as nn
class SimpleNN(nn.Module):
def __init__(self):
super().__init__()
self.fc1 = nn.Linear(2, 10)
self.relu = nn.ReLU()
self.fc2 = nn.Linear(10, 1)
def forward(self, x):
x = self.fc1(x)
x = self.relu(x)
x = self.fc2(x)
return x
# Создаём данные
X = torch.randn(100, 2)
y = (X[:, 0] ** 2 + X[:, 1] ** 2).reshape(-1, 1)
# Обучение
model = SimpleNN()
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
loss_fn = nn.MSELoss()
for epoch in range(100):
output = model(X)
loss = loss_fn(output, y)
# Backpropagation!
optimizer.zero_grad()
loss.backward() # Вычисляет все градиенты автоматически
optimizer.step() # Обновляет параметры
Эффективность Backpropagation
Численное дифференцирование (неэффективно):
# Нужно n forward passes для n параметров
# Сложность: O(n) forward passes
def numerical_gradient(f, x, epsilon=1e-5):
grad = np.zeros_like(x)
for i in range(x.size):
x_plus = x.copy()
x_plus.flat[i] += epsilon
grad.flat[i] = (f(x_plus) - f(x)) / epsilon
return grad
# Для 1M параметров: 1M forward passes!
Backpropagation (эффективно):
- 1 forward pass
- 1 backward pass
- Все градиенты вычисляются за ~O(1) от количества параметров
- Для 1M параметров: всё за 2 passes!
Проблемы и решения
1. Vanishing Gradient Problem
Градиенты могут исчезать при propagation через слои (например, сигмоид: d(σ)/dz ≤ 0.25).
Решение: используйте ReLU вместо сигмоида.
2. Exploding Gradient Problem
Градиенты могут расти экспоненциально.
Решение: Gradient Clipping
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
3. Мертвые нейроны
ReLU даёт 0 для отрицательных значений, нейрон перестаёт обновляться.
Решение: используйте LeakyReLU.
Ключевые выводы
-
Backpropagation — это применение цепного правила в обратном направлении через сеть
-
Два этапа: Forward pass (вычисляем loss), Backward pass (вычисляем градиенты)
-
Эффективность: все градиенты за 1 обратный проход, в отличие от O(n) численного дифференцирования
-
Автоматизация: в PyTorch/TensorFlow это делается автоматически через
.backward() -
Вычислительный граф: позволяет эффективно вычислять производные сложных функций
-
Проблемы: vanishing gradients, exploding gradients, мертвые нейроны (всё имеет решения)
-
На практике: используйте готовые фреймворки, но понимайте математику!