.. _sec_classification: Базовая модель классификации ============================ Вы могли заметить, что реализации с нуля и лаконичная реализация с использованием функциональности фреймворка были довольно похожи в случае регрессии. То же самое верно и для классификации. Поскольку многие модели в этой книге решают задачи классификации, имеет смысл добавить функциональность, специально предназначенную для этого случая. В этом разделе представлена базовый класс для моделей классификации, который упростит дальнейший код. .. raw:: latex \diilbookstyleinputcell .. code:: python import torch from d2l import torch as d2l Класс ``Classifier`` -------------------- Ниже мы определяем класс ``Classifier``. В методе ``validation_step`` мы сообщаем как значение функции потерь, так и точность классификации на валидационном батче. Мы выполняем обновление для каждого ``num_val_batches``-го батча. Это дает преимущество в виде усреднённых значений функции потерь и точности по всем валидационным данным. Эти усреднённые числа не совсем точны, если последний батч содержит меньше примеров, но мы игнорируем эту незначительную разницу, чтобы сохранить код простым. .. raw:: latex \diilbookstyleinputcell .. code:: python 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) По умолчанию мы используем оптимизатор стохастического градиентного спуска, работающий с мини-пакетами, как и в случае с линейной регрессией. .. raw:: latex \diilbookstyleinputcell .. code:: python @d2l.add_to_class(d2l.Module) #@save def configure_optimizers(self): return torch.optim.SGD(self.parameters(), lr=self.lr) Точность -------- Имея предсказанное распределение вероятностей ``y_hat``, мы обычно выбираем класс с наибольшей вероятностью, когда требуется сделать однозначное предсказание. Действительно, во многих приложениях необходимо сделать выбор. Например, Gmail должен отнести письмо к одной из категорий: «Основные», «Социальные», «Обновления», «Форумы» или «Спам». Хотя внутри системы могут вычисляться вероятности, в итоге приходится выбрать один из классов. Когда предсказания совпадают с истинным классом метки ``y``, они считаются верными. Точность классификации — это доля всех предсказаний, которые оказались правильными. Хотя оптимизировать точность напрямую может быть сложно (она не является дифференцируемой), это часто та мера производительности, которая интересует нас больше всего. Она часто *является* ключевой величиной в бенчмарках. Поэтому при обучении классификаторов мы почти всегда будем сообщать о ней. Точность вычисляется следующим образом. Сначала, если ``y_hat`` является матрицей, мы предполагаем, что вторая размерность хранит оценки предсказаний для каждого класса. Мы используем ``argmax`` для определения предсказанного класса по индексу максимального элемента в каждой строке. Затем мы поэлементно сравниваем предсказанный класс с истинными значениями ``y`` . Поскольку оператор равенства ``==`` чувствителен к типам данных, мы приводим тип данных ``y_hat`` к типу данных ``y``. В результате получается тензор, содержащий элементы 0 (ложь) и 1 (истина). Сумма этих элементов даёт количество правильных предсказаний. .. raw:: latex \diilbookstyleinputcell .. code:: python @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 Итог ---- Классификация — достаточно распространённая задача, чтобы выделить для неё отдельные вспомогательные функции. Центральное место в классификации занимает *точность* классификатора. Обратите внимание, что, хотя мы часто ориентируемся прежде всего на точность, обучение классификаторов направлено на оптимизацию различных других целей по статистическим и вычислительным причинам. Тем не менее, независимо от того, какая функция потерь минимизировалась в процессе обучения, полезно иметь вспомогательный метод для эмпирической оценки точности нашего классификатора. Упражнения ---------- 1. Обозначим через :math:`L_\textrm{v}` функцию потерь на валидационной выборке, а через :math:`L_\textrm{v}^\textrm{q}` — её быструю и грубую оценку, вычисленную усреднением функции потерь в данном разделе. Наконец, обозначим через :math:`l_\textrm{v}^\textrm{b}` функцию потерь на последнем мини-батче. Выразите :math:`L_\textrm{v}` через :math:`L_\textrm{v}^\textrm{q}`, :math:`l_\textrm{v}^\textrm{b}`, а также размеры примера и мини-батча. 2. Докажите, что быстрая и грязная оценка :math:`L_\textrm{v}^\textrm{q}` является несмещенной. То есть покажите, что :math:`E[L_\textrm{v}] = E[L_\textrm{v}^\textrm{q}]`. Почему вы все равно можете захотеть использовать :math:`L_\textrm{v}`? 3. Рассматривая функцию потерь для многоклассовой классификации, где :math:`l(y,y')` обозначает штраф за предсказание класса :math:`y'` при истинном классе :math:`y`, и имея априорную вероятность :math:`p(y \mid x)`, сформулируйте правило оптимального выбора класса :math:`y'`. Подсказка: выразите ожидаемые потери, используя :math:`l` и :math:`p(y \mid x)`. `Обсуждения `__