def forward(self, x):

x = torch.relu(self.fc1(x))

x = torch.sigmoid(self.fc2(x))

return x

# Инициализация сети, функции потерь и оптимизатора

model = SimpleNet()

criterion = nn.BCELoss() # Binary Cross Entropy Loss

optimizer = optim.SGD(model.parameters(), lr=0.1)

# Обучение

epochs = 1000

losses = []

for epoch in range(epochs):

# Прямой проход

outputs = model(X_tensor)

loss = criterion(outputs, Y_tensor)

# Обратное распространение и оптимизация

optimizer.zero_grad() # очистка градиентов

loss.backward() # вычисление градиентов

optimizer.step() # обновление весов

losses.append(loss.item())

if (epoch+1) % 100 == 0:

print(f'Epoch [{epoch+1}/{epochs}], Loss: {loss.item():.4f}')

# График функции потерь

plt.plot(losses)

plt.xlabel('Epoch')

plt.ylabel('Loss')

plt.title('Процесс обучения')

plt.show()

# Визуализация результатов

with torch.no_grad():

predictions = model(X_tensor).round()

plt.scatter(X[:, 0], X[:, 1], c=predictions.reshape(-1), cmap='coolwarm', marker='o', edgecolors='k')

plt.title("Классификация точек")

plt.show()

```

Пояснение к коду

1. Генерация данных: Мы создали случайные точки и разделили их на два класса в зависимости от их положения относительно прямой (x + y = 0).

2. Модель: Простая нейросеть с двумя слоями – входной слой с 2 нейронами (для двух координат) и один скрытый слой на 4 нейрона. На выходе – один нейрон с функцией активации `sigmoid`, который предсказывает вероятность принадлежности к классу.

3. Функция ошибки: Используем `BCELoss` (Binary Cross Entropy), поскольку это подходящий критерий для задач бинарной классификации.

4. Оптимизация: Параметры сети оптимизируются методом стохастического градиентного спуска (SGD).

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

6. Визуализация: Построение графика потерь для наблюдения за процессом обучения и визуализация предсказаний.

Результат

График потерь демонстрирует снижение ошибки, а классификация точек показывает, что сеть успешно научилась разделять классы, корректируя веса с каждым циклом обучения, основываясь на ошибках.

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


Градиентный спуск и оптимизация параметров


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

Градиентный спуск бывает нескольких типов:

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

– Стохастический градиентный спуск (SGD): Здесь обновление весов происходит на каждом отдельном примере, что делает обучение более быстрым, но с шумом, так как веса могут изменяться от случая к случаю.

– Мини-batch градиентный спуск: Здесь данные разделяются на небольшие группы (мини-батчи), на основе которых рассчитывается ошибка и корректируются веса, что позволяет использовать преимущества обоих методов.

Оптимизация параметров сети включает выбор скорости обучения и других гиперпараметров, таких как момент, чтобы корректировка весов происходила достаточно быстро, но без переборов. Существуют также адаптивные методы оптимизации, такие как Adam и RMSprop, которые динамически настраивают скорость обучения для каждого веса, учитывая историю изменений, что позволяет избежать проблем с оптимизацией и улучшает эффективность обучения.