# Настройка функции потерь и оптимизатора

criterion = nn.MSELoss() # Функция потерь (например, для регрессии)

optimizer = optim.Adam(model.parameters(), lr=0.001)

# Пример данных для обучения

inputs = torch.randn(32, 5, input_size) # Батч из 32 последовательностей длиной 5 с входным размером 10

labels = torch.randn(32, output_size) # Соответствующие метки

# Обнуление градиентов

optimizer.zero_grad()

# Прямой проход и вычисление потерь

outputs = model(inputs)

loss = criterion(outputs, labels)

# Обратное распространение

loss.backward()

# Применение Gradient Clipping

torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0) # Ограничение градиентов

# Шаг оптимизации

optimizer.step()

print("Значение функции потерь:", loss.item())

```

Объяснение кода:

1. Gradient Clipping:

– `torch.nn.utils.clip_grad_norm_` применяет ограничение к норме градиентов. В данном случае, `max_norm=1.0` означает, что если норма градиента превышает 1.0, она будет уменьшена до этого значения.

– Это предотвращает взрыв градиентов, когда их значения становятся слишком большими, сохраняя процесс обучения стабильным.

2. Применение в RNN:

– Этот метод особенно эффективен в рекуррентных сетях, таких как `SimpleRNN`, где ошибка распространяется через несколько временных шагов, увеличивая риск взрыва градиентов.

3. Когда применять Gradient Clipping:

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

Эти методы помогают сделать обучение нейронных сетей более стабильным и эффективным, особенно при работе с глубокими и рекуррентными архитектурами.

2.4. Алгоритмы оптимизации
2.4.1. Основы градиентного спуска

Градиентный спуск – это способ обучения нейронных сетей, который помогает сети подбирать оптимальные значения весов, чтобы минимизировать ошибки. Представьте, что мы находимся на вершине холма и хотим спуститься в самую низкую точку, которая символизирует минимальную ошибку сети. На каждом шаге мы смотрим вокруг и выбираем направление, которое ведет вниз (градиент), и немного продвигаемся в этом направлении. Шаги, которые мы делаем, называются скоростью обучения. Если шаги слишком большие, мы можем перескочить через низину и не достигнуть цели, а если слишком маленькие, спуск займет очень много времени.

Виды градиентного спуска

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


1. Пакетный градиентный спуск:

– Здесь мы вычисляем обновление весов, используя весь набор данных сразу. Это значит, что мы рассматриваем все примеры (например, все изображения или тексты), обучаемся на них и только после этого обновляем веса.

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

Пример использования пакетного градиентного спуск в Python с использованием библиотеки PyTorch. В этом примере используется весь набор данных для вычисления обновления весов за каждый шаг обучения.

Предположим, у нас есть задача классификации изображений, и мы используем MNIST – набор данных, содержащий изображения рукописных цифр.

```python

import torch

import torch.nn as nn

import torch.optim as optim

from torch.utils.data import DataLoader

from torchvision import datasets, transforms

# Определяем простую нейронную сеть

class SimpleNet(nn.Module):

def __init__(self):

super(SimpleNet, self).__init__()