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

Как происходит обучение классических нейросетей?

1.0 Junior🔥 161 комментариев
#Машинное обучение

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

🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)

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

Как происходит обучение классических нейросетей

Обучение нейросетей основано на алгоритме обратного распространения ошибки (Backpropagation) и градиентном спуске. Разберу весь процесс пошагово.

Архитектура простой нейросети

Классическая нейросеть состоит из:

  1. Входной слой (input layer) — принимает признаки
  2. Скрытые слои (hidden layers) — обрабатывают информацию
  3. Выходной слой (output layer) — выдает предсказание
Вход (x1, x2, x3) → Скрытый слой 1 → Скрытый слой 2 → Выход (y)

Каждый нейрон выполняет операцию:

z = w1*x1 + w2*x2 + ... + wn*xn + b
a = activation_function(z)

Где:

  • w — веса (weights)
  • b — смещение (bias)
  • activation_function — функция активации (ReLU, Sigmoid, Tanh)

Функция потерь (Loss Function)

Для классификации используем cross-entropy, для регрессии — MSE:

# Cross-entropy (классификация)
from tensorflow.keras.losses import CategoricalCrossentropy
loss = CategoricalCrossentropy()
# loss = -sum(y_true * log(y_pred))

# MSE (регрессия)
from tensorflow.keras.losses import MeanSquaredError
loss = MeanSquaredError()
# loss = mean((y_true - y_pred)^2)

Алгоритм обучения: Backpropagation (Обратное распространение)

Шаг 1: Forward Pass (прямой проход)

Данные проходят через сеть от входа к выходу:

import numpy as np

class NeuralNetwork:
    def __init__(self, layer_sizes):
        self.weights = []
        self.biases = []
        
        # Инициализация весов случайно (Xavier initialization)
        for i in range(len(layer_sizes) - 1):
            w = np.random.randn(layer_sizes[i], layer_sizes[i+1]) * 0.01
            b = np.zeros((1, layer_sizes[i+1]))
            self.weights.append(w)
            self.biases.append(b)
    
    def relu(self, z):
        return np.maximum(0, z)
    
    def relu_derivative(self, z):
        return (z > 0).astype(float)
    
    def softmax(self, z):
        exp_z = np.exp(z - np.max(z, axis=1, keepdims=True))
        return exp_z / np.sum(exp_z, axis=1, keepdims=True)
    
    def forward(self, X):
        self.activations = [X]  # Сохраняем для backprop
        self.z_values = []      # Сохраняем z для backprop
        
        a = X
        # Скрытые слои
        for i in range(len(self.weights) - 1):
            z = np.dot(a, self.weights[i]) + self.biases[i]
            a = self.relu(z)
            self.z_values.append(z)
            self.activations.append(a)
        
        # Выходной слой (softmax для классификации)
        z = np.dot(a, self.weights[-1]) + self.biases[-1]
        a = self.softmax(z)
        self.z_values.append(z)
        self.activations.append(a)
        
        return a

Шаг 2: Вычисление потерь

def compute_loss(self, y_pred, y_true):
    m = y_true.shape[0]
    
    # Cross-entropy
    epsilon = 1e-7  # Избегаем log(0)
    loss = -np.sum(y_true * np.log(y_pred + epsilon)) / m
    return loss

Шаг 3: Backward Pass (обратный проход)

Вычисляем градиенты функции потерь по каждому весу:

def backward(self, y_true, learning_rate=0.01):
    m = y_true.shape[0]
    
    # Выходной слой
    delta = (self.activations[-1] - y_true) / m  # dL/dz
    
    # Идем слой за слоем в обратном направлении
    for i in range(len(self.weights) - 1, -1, -1):
        # Градиент по весам
        dW = np.dot(self.activations[i].T, delta)
        # Градиент по смещению
        db = np.sum(delta, axis=0, keepdims=True)
        
        # Обновляем веса (Gradient Descent)
        self.weights[i] -= learning_rate * dW
        self.biases[i] -= learning_rate * db
        
        # Если не последний слой — считаем градиент для предыдущего слоя
        if i > 0:
            delta = np.dot(delta, self.weights[i].T) * \
                    self.relu_derivative(self.z_values[i-1])

Полный цикл обучения (Training Loop)

def train(self, X, y, epochs=100, batch_size=32, learning_rate=0.01):
    n_samples = X.shape[0]
    
    for epoch in range(epochs):
        # Перемешиваем данные
        indices = np.random.permutation(n_samples)
        X_shuffled = X[indices]
        y_shuffled = y[indices]
        
        total_loss = 0
        
        # Мини-батчи (Stochastic Gradient Descent)
        for i in range(0, n_samples, batch_size):
            X_batch = X_shuffled[i:i+batch_size]
            y_batch = y_shuffled[i:i+batch_size]
            
            # Forward pass
            y_pred = self.forward(X_batch)
            
            # Compute loss
            loss = self.compute_loss(y_pred, y_batch)
            total_loss += loss
            
            # Backward pass
            self.backward(y_batch, learning_rate)
        
        avg_loss = total_loss / (n_samples // batch_size)
        print(f"Epoch {epoch+1}/{epochs} - Loss: {avg_loss:.4f}")

Визуализация процесса

Epoch 1: Loss = 2.3
Epoch 2: Loss = 1.8
Epoch 3: Loss = 1.4
Epoch 4: Loss = 1.1
Epoch 5: Loss = 0.8  ← Веса обновляются, ошибка уменьшается
...
Epoch 100: Loss = 0.1

Оптимизаторы (Optimizers)

1. SGD (Stochastic Gradient Descent)

# Базовый вариант
weights -= learning_rate * gradient

2. Momentum

# Учитывает направление движения
velocity = momentum * velocity + learning_rate * gradient
weights -= velocity

3. Adam (Adaptive Moment Estimation)

from tensorflow.keras.optimizers import Adam

# Комбинирует идеи Momentum и RMSprop
# Адаптивная скорость обучения для каждого параметра
optimizer = Adam(learning_rate=0.001)

Пример с TensorFlow/Keras

import tensorflow as tf
from tensorflow.keras import layers, models
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.losses import SparseCategoricalCrossentropy

# Построение модели
model = models.Sequential([
    layers.Dense(128, activation='relu', input_shape=(28*28,)),
    layers.Dropout(0.2),
    layers.Dense(64, activation='relu'),
    layers.Dropout(0.2),
    layers.Dense(10, activation='softmax')
])

# Компиляция модели
model.compile(
    optimizer=Adam(learning_rate=0.001),
    loss=SparseCategoricalCrossentropy(),
    metrics=['accuracy']
)

# Обучение
history = model.fit(
    X_train, y_train,
    epochs=50,
    batch_size=32,
    validation_split=0.2,
    verbose=1
)

# Оценка
test_loss, test_acc = model.evaluate(X_test, y_test)
print(f"Test accuracy: {test_acc:.4f}")

Важные концепции

1. Learning Rate

Очень маленький (0.00001): медленное обучение, может застрять
Оптимальный (0.01): быстрое и стабильное обучение
Очень большой (1.0): скачки и нестабильность

2. Batch Size

Маленький (1): шумный градиент, но быстрее адаптация
Оптимальный (32-128): баланс скорости и стабильности
Большой (1000+): гладкий градиент, но риск застрять в локальном минимуме

3. Epochs vs Iterations

# Epoch — один проход через весь датасет
# Iteration — один обновление весов на одном батче

n_samples = 10000
batch_size = 32
epochs = 50

iterations_per_epoch = n_samples // batch_size  # 312
total_iterations = epochs * iterations_per_epoch  # 15600

4. Vanishing Gradient Problem

При обратном распространении градиенты могут становиться очень маленькими в глубоких сетях:

# Решение 1: ReLU вместо Sigmoid
model.add(layers.Dense(128, activation='relu'))  # ✓ Хорошо
model.add(layers.Dense(128, activation='sigmoid'))  # ✗ Плохо для глубоких сетей

# Решение 2: Batch Normalization
model.add(layers.Dense(128, activation='relu'))
model.add(layers.BatchNormalization())

# Решение 3: Residual connections (Skip connections)
model.add(layers.Dense(128, activation='relu'))
model.add(layers.Dense(128))
model.add(layers.Add())  # Добавляем оригинальный вход

Итог процесса обучения

  1. Forward pass — примеры проходят через сеть
  2. Loss calculation — вычисляем ошибку
  3. Backward pass — считаем градиенты
  4. Weight update — обновляем веса на основе градиентов
  5. Повторяем — 50-100 эпох пока loss не сойдется

Ключевые этапы:

  • Инициализация весов случайно
  • Перемешивание данных для каждой эпохи
  • Использование батчей для эффективности
  • Мониторинг loss на валидационном наборе
  • Ранняя остановка (early stopping) если валидация не улучшается
Как происходит обучение классических нейросетей? | PrepBro