4.3. Базовая модель классификации¶
Вы могли заметить, что реализации с нуля и лаконичная реализация с использованием функциональности фреймворка были довольно похожи в случае регрессии. То же самое верно и для классификации. Поскольку многие модели в этой книге решают задачи классификации, имеет смысл добавить функциональность, специально предназначенную для этого случая. В этом разделе представлена базовый класс для моделей классификации, который упростит дальнейший код.
import torch
from d2l import torch as d2l
4.3.1. Класс Classifier¶
Ниже мы определяем класс Classifier. В методе validation_step мы
сообщаем как значение функции потерь, так и точность классификации на
валидационном батче. Мы выполняем обновление для каждого
num_val_batches-го батча. Это дает преимущество в виде усреднённых
значений функции потерь и точности по всем валидационным данным. Эти
усреднённые числа не совсем точны, если последний батч содержит меньше
примеров, но мы игнорируем эту незначительную разницу, чтобы сохранить
код простым.
class Classifier(d2l.Module): #@save
"""The base class of classification models."""
def validation_step(self, batch):
Y_hat = self(*batch[:-1])
self.plot('loss', self.loss(Y_hat, batch[-1]), train=False)
self.plot('acc', self.accuracy(Y_hat, batch[-1]), train=False)
По умолчанию мы используем оптимизатор стохастического градиентного спуска, работающий с мини-пакетами, как и в случае с линейной регрессией.
@d2l.add_to_class(d2l.Module) #@save
def configure_optimizers(self):
return torch.optim.SGD(self.parameters(), lr=self.lr)
4.3.2. Точность¶
Имея предсказанное распределение вероятностей y_hat, мы обычно
выбираем класс с наибольшей вероятностью, когда требуется сделать
однозначное предсказание. Действительно, во многих приложениях
необходимо сделать выбор. Например, Gmail должен отнести письмо к одной
из категорий: «Основные», «Социальные», «Обновления», «Форумы» или
«Спам». Хотя внутри системы могут вычисляться вероятности, в итоге
приходится выбрать один из классов.
Когда предсказания совпадают с истинным классом метки y, они
считаются верными. Точность классификации — это доля всех предсказаний,
которые оказались правильными. Хотя оптимизировать точность напрямую
может быть сложно (она не является дифференцируемой), это часто та мера
производительности, которая интересует нас больше всего. Она часто
является ключевой величиной в бенчмарках. Поэтому при обучении
классификаторов мы почти всегда будем сообщать о ней.
Точность вычисляется следующим образом. Сначала, если y_hat является
матрицей, мы предполагаем, что вторая размерность хранит оценки
предсказаний для каждого класса. Мы используем argmax для
определения предсказанного класса по индексу максимального элемента в
каждой строке. Затем мы поэлементно сравниваем предсказанный класс с
истинными значениями y . Поскольку оператор равенства ==
чувствителен к типам данных, мы приводим тип данных y_hat к типу
данных y. В результате получается тензор, содержащий элементы 0
(ложь) и 1 (истина). Сумма этих элементов даёт количество правильных
предсказаний.
@d2l.add_to_class(Classifier) #@save
def accuracy(self, Y_hat, Y, averaged=True):
"""Compute the number of correct predictions."""
Y_hat = Y_hat.reshape((-1, Y_hat.shape[-1]))
preds = Y_hat.argmax(axis=1).type(Y.dtype)
compare = (preds == Y.reshape(-1)).type(torch.float32)
return compare.mean() if averaged else compare
4.3.3. Итог¶
Классификация — достаточно распространённая задача, чтобы выделить для неё отдельные вспомогательные функции. Центральное место в классификации занимает точность классификатора. Обратите внимание, что, хотя мы часто ориентируемся прежде всего на точность, обучение классификаторов направлено на оптимизацию различных других целей по статистическим и вычислительным причинам. Тем не менее, независимо от того, какая функция потерь минимизировалась в процессе обучения, полезно иметь вспомогательный метод для эмпирической оценки точности нашего классификатора.
4.3.4. Упражнения¶
Обозначим через \(L_\textrm{v}\) функцию потерь на валидационной выборке, а через \(L_\textrm{v}^\textrm{q}\) — её быструю и грубую оценку, вычисленную усреднением функции потерь в данном разделе. Наконец, обозначим через \(l_\textrm{v}^\textrm{b}\) функцию потерь на последнем мини-батче. Выразите \(L_\textrm{v}\) через \(L_\textrm{v}^\textrm{q}\), \(l_\textrm{v}^\textrm{b}\), а также размеры примера и мини-батча.
Докажите, что быстрая и грязная оценка \(L_\textrm{v}^\textrm{q}\) является несмещенной. То есть покажите, что \(E[L_\textrm{v}] = E[L_\textrm{v}^\textrm{q}]\). Почему вы все равно можете захотеть использовать \(L_\textrm{v}\)?
Рассматривая функцию потерь для многоклассовой классификации, где \(l(y,y')\) обозначает штраф за предсказание класса \(y'\) при истинном классе \(y\), и имея априорную вероятность \(p(y \mid x)\), сформулируйте правило оптимального выбора класса \(y'\). Подсказка: выразите ожидаемые потери, используя \(l\) и \(p(y \mid x)\).