Активации и функции потерь

Два недостающих кирпичика: нелинейные активации и функции потерь, по которым модель учится.

Функция потерь (loss) — число, измеряющее, насколько предсказание модели далеко от правильного ответа; именно его минимизирует обучение.

Зачем нужны активации

Если поставить друг за другом два линейных слоя без нелинейности между ними, вся сеть схлопнется в одно линейное преобразование — никакой пользы от глубины. Чтобы сеть могла выражать сложные зависимости, между слоями ставят нелинейную активацию. Самая популярная — ReLU: она оставляет положительные значения как есть, а отрицательные обнуляет.

import torch
import torch.nn as nn

relu = nn.ReLU()
x = torch.tensor([-2.0, -0.5, 0.0, 1.0, 3.0])
print(relu(x))   # tensor([0., 0., 0., 1., 3.])
АктивацияЧто делаетГде применяют
nn.ReLUmax(0, x)скрытые слои (по умолчанию)
nn.Sigmoidсжимает в (0, 1)выход бинарной классификации
nn.Tanhсжимает в (-1, 1)иногда в RNN
nn.Softmaxпревращает в вероятности классовобычно встроен в loss

MSELoss — для регрессии

Когда модель предсказывает число (цену, температуру), ошибку меряют среднеквадратичной: nn.MSELoss усредняет квадраты разностей между предсказанием и целью. Это тот самый MSE из теории.

criterion = nn.MSELoss()

pred   = torch.tensor([2.5, 0.0, 2.0])
target = torch.tensor([3.0, 0.0, 2.0])

loss = criterion(pred, target)
print(loss)   # tensor(0.0833)  = ((0.5^2 + 0 + 0) / 3)

CrossEntropyLoss — для классификации

Для классификации на несколько классов берут nn.CrossEntropyLoss. У неё есть два нюанса, в которых путаются почти все новички:

  • На вход идут «сырые» логиты, а не вероятности. CrossEntropyLoss сама внутри применяет softmax. Поэтому softmax в конце сети ставить не нужно — будет двойное применение.
  • Цель — это индексы классов (целые числа), а не one-hot векторы. Для 3 классов target — это числа 0, 1 или 2.
criterion = nn.CrossEntropyLoss()

# сырые логиты модели: батч из 2 примеров, по 3 класса
logits = torch.tensor([[2.0, 0.5, 0.1],
                       [0.1, 0.2, 3.0]])
# правильные классы как индексы: пример 0 -> класс 0, пример 1 -> класс 2
target = torch.tensor([0, 2])

loss = criterion(logits, target)
print(loss)   # одно число — насколько модель ошиблась

Запомните формулировку: «логиты на вход, индексы классов как цель, softmax внутри». Это снимает 90% ошибок с классификацией.

Как loss связан с обучением

Функция потерь — это тот самый скаляр, на котором мы вызываем .backward(). Цепочка такая: модель выдала предсказание → criterion сравнил с целью и вернул число loss → loss.backward() посчитал градиенты всех весов → оптимизатор сдвинул веса так, чтобы loss стал меньше. В следующем разделе соберём это в полный цикл.

Итог

  • Активации (ReLU и др.) вносят нелинейность; без них стопка линейных слоёв бесполезна.
  • nn.MSELoss — для регрессии (среднеквадратичная ошибка).
  • nn.CrossEntropyLoss — для классификации: на вход логиты, цель — индексы классов, softmax внутри.
  • loss — это скаляр, с которого начинается backprop и обновление весов.
Проверьте себя
1. Что подают на вход nn.CrossEntropyLoss как предсказание модели?
AВероятности после softmax
BСырые логиты — softmax она применяет сама
COne-hot векторы классов
DИндексы предсказанных классов
2. В каком виде задают цель (target) для CrossEntropyLoss?
AOne-hot векторы
BВероятности классов
CЦелочисленные индексы правильных классов
DСтроки с названиями классов
3. Зачем между линейными слоями ставят нелинейную активацию вроде ReLU?
AЧтобы ускорить вычисления
BИначе стопка линейных слоёв схлопнется в одно линейное преобразование
CЧтобы уменьшить число параметров
DАктивации обязательны только на выходе
Поддержать проект