Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

4.4 Выбор модели, недообучение и переобучение

Цель специалиста по машинному обучению — находить закономерности (pattern). Но как понять, что модель действительно нашла закономерность, которая обобщается, а не просто запомнила данные? Например, мы хотим найти закономерность между генетическими данными пациентов и состоянием деменции, где метка принимает значения из множества . Поскольку генетические данные однозначно определяют каждого человека (не считая близнецов), в этой задаче возможно просто запомнить весь набор данных.

Нам не нужна модель, которая рассуждает так: «Это Боб! Я помню его! У него деменция!» Причина проста: когда модель будет развёрнута, ей придётся делать выводы о пациентах, которых она никогда не видела. Только если модель действительно выявила обобщаемую закономерность, её прогнозы будут полезны.

Формально наша цель — найти такие закономерности, которые отражают свойства генеральной совокупности, из которой взят обучающий набор. Если это удаётся, модель может оценивать риск даже для людей, которых она никогда не встречала. Как находить закономерности, которые можно обобщать, — фундаментальный вопрос машинного обучения.

Трудность в том, что при обучении модели мы видим лишь небольшую часть данных. Крупнейший открытый набор изображений содержит около миллиона изображений. Но чаще всего мы учимся на тысячах или десятках тысяч примеров. В крупной больничной системе могут быть доступны сотни тысяч медицинских карт. Работая с ограниченной выборкой, можно столкнуться с такой ситуацией: когда собрано больше данных, выясняется, что ранее найденная очевидная связь не выполняется.

Явление, при котором модель описывает обучающие данные лучше, чем реальную закономерность в генеральной совокупности, называют переобучением (overfitting), а методы борьбы с ним — регуляризацией (regularization). В предыдущих главах некоторые читатели, возможно, уже наблюдали переобучение, экспериментируя с набором Fashion-MNIST. Если менять архитектуру модели или гиперпараметры, то при достаточном числе нейронов, слоёв и эпох обучения модель в конце концов может достичь идеальной точности на обучающем наборе, хотя точность на тестовых данных при этом падает.

4.4.1 Ошибка обучения и ошибка обобщения

Чтобы подробнее разобрать это явление, нужно понимать ошибку обучения и ошибку обобщения. Ошибка обучения (training error) — это ошибка модели, вычисленная на обучающем наборе данных. Ошибка обобщения (generalization error) — это математическое ожидание ошибки модели, когда её применяют к бесконечному числу примеров данных, взятых из того же исходного распределения, что и обучающие данные.

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

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

Аналогично рассмотрим модель, которая отвечает на вопросы, просто заглядывая в таблицу. Если множество допустимых входов дискретно и достаточно мало, то после просмотра многих обучающих примеров такой метод может работать хорошо. Но когда модель сталкивается с никогда не виденными примерами, её результат едва ли лучше случайного угадывания. Дело в том, что пространство входов слишком велико, и невозможно запомнить ответ на каждый возможный вход. Например, рассмотрим полутоновое изображение . Если каждый пиксель может принимать одно из значений яркости, то существует возможных изображений. Это значит, что количество низкоразрешённых полутоновых изображений размером с ноготь намного больше числа атомов во Вселенной. Даже если нам и встретятся такие данные, хранить всю таблицу поиска мы всё равно не сможем.

Наконец, рассмотрим задачу классификации результатов броска монеты (класс 0: орёл, класс 1: решка). Пусть монета честная: тогда, каким бы ни был алгоритм, ошибка обобщения всегда равна . Однако для большинства алгоритмов мы должны ожидать, что ошибка обучения будет меньше (хотя это зависит от везения). Рассмотрим набор данных {0, 1, 1, 1, 0, 1}. Наш алгоритм, не имея дополнительных признаков, будет склонен всегда предсказывать преобладающий класс; по нашей ограниченной выборке им кажется 1. В этом случае модель, которая всегда предсказывает класс 1, даст ошибку , что заметно лучше нашей ошибки обобщения. Но по мере роста объёма данных вероятность того, что доля орлов заметно отклонится от , будет снижаться, и ошибка обучения будет приближаться к ошибке обобщения.

4.4.1.1 Статистическая теория обучения

Поскольку обобщение — фундаментальная проблема машинного обучения, многие математики и теоретики посвятили жизнь изучению её формальной теории. В одноимённой теореме Гливенко и Кантелли вывели скорость, с которой ошибка обучения сходится к ошибке обобщения. В серии новаторских работ Вапник и Червоненкис распространили эту теорию на более широкий класс функций. Эти работы заложили основы статистической теории обучения.

В рассмотренном и далее в книге случае обучения с учителем мы предполагаем, что обучающие и тестовые данные независимо извлечены из одного и того же распределения. Это предположение обычно называют предположением о независимости и одинаковой распределённости (i.i.d. assumption; independent and identically distributed). Оно означает, что процесс выборки данных не «запоминает» предыдущие примеры. Иначе говоря, связь между вторым и третьим извлечёнными примерами не сильнее, чем связь между вторым и двухмиллионным.

Чтобы стать хорошим специалистом по машинному обучению, нужно уметь критически мыслить. Предположения часто уязвимы: несложно придумать ситуации, в которых они не выполняются. Допустим, мы обучили модель предсказания смертельного риска на данных пациентов медицинского центра Калифорнийского университета в Сан-Франциско и применили её к данным пациентов Массачусетской больницы общего профиля. Что тогда произойдёт? Эти два распределения могут заметно различаться. К тому же процесс выборки может зависеть от времени. Например, при классификации тем в микроблогах новостной цикл создаёт временную зависимость обсуждаемых тем, что нарушает предположение о независимости.

Иногда модель продолжает работать очень хорошо, даже если предположение о независимой и одинаковой распределённости нарушено незначительно. Например, существует много полезных инструментов, уже применяемых на практике: распознавание лиц, распознавание речи и машинный перевод. В конце концов, почти в любом реальном применении есть хотя бы некоторые отклонения от этого предположения.

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

В следующих разделах мы будем обсуждать проблемы, возникающие из-за нарушения предположения о независимой и одинаковой распределённости. Пока же даже при принятии этого предположения понимать обобщаемость — непростая задача. Более того, теоретические основы, объясняющие обобщающую способность глубоких нейронных сетей, по-прежнему ставят в тупик выдающихся учёных в области теории обучения.

Когда мы обучаем модель, мы стремимся найти функцию, которая как можно лучше соответствует обучающим данным. Но если она подгоняется «слишком хорошо» и не может хорошо обобщаться на невидимые данные, возникает переобучение. Именно этого мы хотим избегать или контролировать. В глубоком обучении есть много эвристических приёмов, направленных на предотвращение переобучения.

4.4.1.2 Сложность модели

Если модель простая, а данных много, мы ожидаем, что ошибка обобщения будет близка к ошибке обучения. Если модель сложнее, а примеров меньше, мы ожидаем, что ошибка обучения уменьшится, а ошибка обобщения возрастёт. Что именно делает модель сложной, — непростой вопрос. Способность модели хорошо обобщаться зависит от многих факторов. Например, модель с большим числом параметров может считаться более сложной, а модель, у которой параметры могут принимать значения из более широкого диапазона, — тоже более сложной. Для нейронных сетей обычно считается, что модель, которой нужно больше итераций обучения, сложнее, а модель, для которой нужна ранняя остановка (early stopping), то есть меньшее число эпох обучения, — проще.

Сравнивать сложность принципиально разных классов моделей (например, деревьев решений и нейронных сетей) трудно. Пока полезно простое эмпирическое правило: статистики считают сложной модель, которая легко объясняет произвольные факты, а модель с ограниченной выразительностью, но хорошо объясняющая данные, может быть полезнее на практике. В философии это тесно связано с критерием фальсифицируемости научных теорий Поппера: теория хороша, если она согласуется с данными и существуют конкретные проверки, которые могли бы доказать её неверность. Это важно, потому что все статистические оценки — это индуктивные выводы post hoc. То есть мы делаем оценки после наблюдения фактов, и поэтому они подвержены корреляционным ошибкам. Сейчас отложим философию и займёмся более практичными вопросами.

Чтобы дать интуитивное представление, мы выделим несколько факторов, которые обычно влияют на обобщающую способность модели.

  1. Число настраиваемых параметров. Когда число настраиваемых параметров (иногда его называют числом степеней свободы) велико, модель, как правило, склонна к переобучению.
  2. Значения параметров. Когда веса могут принимать значения из широкого диапазона, модель может оказаться более склонной к переобучению.
  3. Число обучающих примеров. Даже простая модель может переобучиться на наборе из одного-двух примеров. А чтобы переобучиться на наборе из миллионов примеров, нужна очень гибкая модель.

4.4.2 Выбор модели

В машинном обучении мы обычно оцениваем несколько кандидатов и затем выбираем итоговую модель. Этот процесс называется выбором модели (model selection). Иногда сравниваемые модели принципиально различаются (например, дерево решений и линейная модель). В других случаях мы сравниваем модели одного и того же класса с разными значениями гиперпараметров.

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

4.4.2.1 Проверочный набор

По сути, пока мы не определили все гиперпараметры, мы не хотим использовать тестовый набор. Если во время выбора модели использовать тестовые данные, есть риск переобучиться на тестовых данных, а это серьёзная проблема. Если мы переобучились на обучающих данных, оценку на тестовых данных можно использовать, чтобы это заметить. Но если мы переобучились на тестовых данных, как мы узнаем об этом?

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

На практике ситуация сложнее. В идеале тестовые данные нужно использовать один раз — чтобы оценить лучшую модель или сравнить несколько моделей. Но на деле тестовые данные редко выбрасывают после одного использования. У нас редко бывает достаточно данных, чтобы для каждого этапа экспериментов брать новый тестовый набор.

Частое решение этой проблемы — разделить данные на три части: помимо обучающего и тестового наборов выделить проверочный набор данных (validation dataset), или проверочный набор (validation set). Но на практике граница между проверочными и тестовыми данными часто размыта. Если явно не сказано иное, в экспериментах этой книги мы фактически используем наборы, которые правильнее было бы назвать обучающими и проверочными, и настоящего тестового набора нет. Поэтому точность, которую книга приводит для каждого эксперимента, — это точность на проверочном наборе, а не на тестовом.

4.4.2.2 -кратная перекрёстная проверка

Когда обучающих данных мало, для построения достаточно большого проверочного набора их может не хватить. Популярное решение этой проблемы — -кратная перекрёстная проверка (K-fold cross-validation). Здесь исходные обучающие данные разбиваются на непересекающихся подмножеств. Затем выполняются обучений и проверок модели: каждый раз модель обучается на подмножествах и проверяется на оставшемся подмножестве (том, которое не использовалось для обучения в этом проходе). В итоге ошибки обучения и проверки оцениваются как среднее по экспериментам.

4.4.3 Недообучение или переобучение?

Когда мы сравниваем ошибки обучения и проверки, нужно обращать внимание на два типичных случая. Во-первых, стоит обратить внимание на ситуацию, когда и ошибка обучения, и ошибка проверки велики, но разница между ними невелика. Если модель не может снизить ошибку обучения, это может означать, что она слишком простая (то есть недостаточно выразительна) и не способна уловить закономерность, которую пытается выучить. Кроме того, поскольку ошибка обобщения (разница между ошибками обучения и проверки) мала, у нас есть основания полагать, что более сложная модель могла бы снизить ошибку обучения. Это явление называют недообучением (underfitting).

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

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

4.4.3.1 Сложность модели

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

Это просто задача линейной регрессии: признаками служат степени , весами — , а смещение задаётся (поскольку для всех ). Поскольку это линейная регрессия, в качестве функции потерь можно использовать квадратичную ошибку.

Многочлен высокой степени гораздо сложнее многочлена низкой степени. У многочлена высокой степени больше параметров и шире выбор функций модели. Поэтому при фиксированном обучающем наборе данных ошибка обучения многочлена высокой степени всегда должна быть не больше (в худшем случае такой же), чем у многочлена низкой степени. Более того, если примеры содержат разные значения , многочлен степени, равной числу примеров, может идеально подогнаться под обучающий набор. На рис. 4.4.1 показана связь между степенью многочлена и недообучением, а также переобучением.

Рис. 4.4.1. Влияние сложности модели на недообучение и переобучение

4.4.3.2 Размер набора данных

Ещё один важный фактор — размер набора данных. Чем меньше обучающих примеров, тем выше вероятность переобучения и тем оно сильнее. С ростом объёма обучающих данных ошибка обобщения обычно уменьшается. Кроме того, в общем случае больше данных не бывает лишним. Для фиксированной задачи и распределения данных между сложностью модели и размером набора данных обычно есть связь. Имея больше данных, мы можем попытаться подогнать более сложную модель. Способность подгонять более сложные модели может быть полезна. Если данных недостаточно, более простые модели могут оказаться полезнее. Для многих задач глубокое обучение превосходит линейные модели только при наличии тысяч обучающих примеров. В известной мере нынешний подъём глубокого обучения обязан дешёвым системам хранения, подключённым устройствам и огромным наборам данных, которые дала цифровая экономика.

4.4.4 Полиномиальная регрессия

Теперь мы можем исследовать эти понятия на примере подгонки многочленов.

MXNet:

from d2l import mxnet as d2l
from mxnet import gluon, np, npx
from mxnet.gluon import nn
import math
npx.set_np()

PyTorch:

from d2l import torch as d2l
import torch
from torch import nn
import numpy as np
import math

TensorFlow:

from d2l import tensorflow as d2l
import tensorflow as tf
import numpy as np
import math

PaddlePaddle:

from d2l import paddle as d2l
import warnings
warnings.filterwarnings("ignore")
import paddle
from paddle import nn
import numpy as np
import math

4.4.4.1 Генерация набора данных

По заданному мы будем генерировать метки обучающих и тестовых данных с помощью следующего многочлена третьей степени:

Шумовой член распределён нормально, с математическим ожиданием 0 и стандартным отклонением 0.1. В процессе оптимизации обычно хочется избегать очень больших значений градиентов и функции потерь. Поэтому мы масштабируем признаки от к . Это позволяет избежать очень больших значений показателей при больших . Мы сгенерируем по 100 примеров для обучающего и для тестового наборов.

max_degree = 20  # Максимальная степень многочлена
n_train, n_test = 100, 100  # Размеры обучающего и тестового наборов данных
true_w = np.zeros(max_degree)  # Выделить большой объём памяти
true_w[0:4] = np.array([5, 1.2, -3.4, 5.6])

features = np.random.normal(size=(n_train + n_test, 1))
np.random.shuffle(features)
poly_features = np.power(features, np.arange(max_degree).reshape(1, -1))
for i in range(max_degree):
    poly_features[:, i] /= math.gamma(i + 1)  # gamma(n)=(n-1)!
# Размерность labels: (n_train+n_test,)
labels = np.dot(poly_features, true_w)
labels += np.random.normal(scale=0.1, size=labels.shape)

Аналогично одночлены, хранящиеся в poly_features, перемасштабированы гамма-функцией, где . Посмотрим на первые два примера из сгенерированного набора данных. Первое значение — постоянный признак, соответствующий смещению.

PyTorch, TensorFlow, PaddlePaddle:

# Преобразовать NumPy ndarray в tensor
true_w, features, poly_features, labels = [d2l.tensor(x, dtype=
    d2l.float32) for x in [true_w, features, poly_features, labels]]
features[:2], poly_features[:2, :], labels[:2]

4.4.4.2 Обучение и тестирование модели

Сначала реализуем функцию, которая оценивает потери модели на заданном наборе данных.

MXNet, TensorFlow:

def evaluate_loss(net, data_iter, loss):  #@save
    """Оценить потери модели на заданном наборе данных"""
    metric = d2l.Accumulator(2)  # Сумма потерь, количество примеров
    for X, y in data_iter:
        l = loss(net(X), y)
        metric.add(d2l.reduce_sum(l), d2l.size(l))
    return metric[0] / metric[1]

PyTorch:

def evaluate_loss(net, data_iter, loss):  #@save
    """Оценить потери модели на заданном наборе данных"""
    metric = d2l.Accumulator(2)  # Сумма потерь, количество примеров
    for X, y in data_iter:
        out = net(X)
        y = d2l.reshape(y, out.shape)
        l = loss(out, y)
        metric.add(d2l.reduce_sum(l), d2l.size(l))
    return metric[0] / metric[1]

PaddlePaddle:

def evaluate_loss(net, data_iter, loss):  #@save
    """Оценить потери модели на заданном наборе данных."""
    metric = d2l.Accumulator(2)  # Сумма потерь, количество примеров
    for X, y in data_iter:
        out = net(X)
        y = y.reshape(out.shape)
        l = loss(out, y)
        metric.add(l.sum(), l.numel())
    return metric[0] / metric[1]

Теперь определим функцию обучения.

MXNet:

def train(train_features, test_features, train_labels, test_labels,
          num_epochs=400):
    loss = gluon.loss.L2Loss()
    net = nn.Sequential()
    # Смещение не задаём, так как оно уже реализовано в полиномиальных признаках
    net.add(nn.Dense(1, use_bias=False))
    net.initialize()
    batch_size = min(10, train_labels.shape[0])
    train_iter = d2l.load_array((train_features, train_labels), batch_size)
    test_iter = d2l.load_array((test_features, test_labels), batch_size,
                               is_train=False)
    trainer = gluon.Trainer(net.collect_params(), 'sgd',
                            {'learning_rate': 0.01})
    animator = d2l.Animator(xlabel='эпоха', ylabel='потери', yscale='log',
                            xlim=[1, num_epochs], ylim=[1e-3, 1e2],
                            legend=['обучение', 'тест'])
    for epoch in range(num_epochs):
        d2l.train_epoch_ch3(net, train_iter, loss, trainer)
        if epoch == 0 or (epoch + 1) % 20 == 0:
            animator.add(epoch + 1, (evaluate_loss(net, train_iter, loss),
                                     evaluate_loss(net, test_iter, loss)))
    print('вес:', net[0].weight.data().asnumpy())

PyTorch:

def train(train_features, test_features, train_labels, test_labels,
          num_epochs=400):
    loss = nn.MSELoss(reduction='none')
    input_shape = train_features.shape[-1]
    # Смещение не задаём, так как оно уже реализовано в полиномиальных признаках
    net = nn.Sequential(nn.Linear(input_shape, 1, bias=False))
    batch_size = min(10, train_labels.shape[0])
    train_iter = d2l.load_array((train_features, train_labels.reshape(-1,1)),
                                batch_size)
    test_iter = d2l.load_array((test_features, test_labels.reshape(-1,1)),
                               batch_size, is_train=False)
    trainer = torch.optim.SGD(net.parameters(), lr=0.01)
    animator = d2l.Animator(xlabel='эпоха', ylabel='потери', yscale='log',
                            xlim=[1, num_epochs], ylim=[1e-3, 1e2],
                            legend=['обучение', 'тест'])
    for epoch in range(num_epochs):
        d2l.train_epoch_ch3(net, train_iter, loss, trainer)
        if epoch == 0 or (epoch + 1) % 20 == 0:
            animator.add(epoch + 1, (evaluate_loss(net, train_iter, loss),
                                     evaluate_loss(net, test_iter, loss)))
    print('вес:', net[0].weight.data.numpy())

TensorFlow:

def train(train_features, test_features, train_labels, test_labels,
          num_epochs=400):
    loss = tf.losses.MeanSquaredError()
    input_shape = train_features.shape[-1]
    # Смещение не задаём, так как оно уже реализовано в полиномиальных признаках
    net = tf.keras.Sequential()
    net.add(tf.keras.layers.Dense(1, use_bias=False))
    batch_size = min(10, train_labels.shape[0])
    train_iter = d2l.load_array((train_features, train_labels), batch_size)
    test_iter = d2l.load_array((test_features, test_labels), batch_size,
                               is_train=False)
    trainer = tf.keras.optimizers.SGD(learning_rate=.01)
    animator = d2l.Animator(xlabel='эпоха', ylabel='потери', yscale='log',
                            xlim=[1, num_epochs], ylim=[1e-3, 1e2],
                            legend=['обучение', 'тест'])
    for epoch in range(num_epochs):
        d2l.train_epoch_ch3(net, train_iter, loss, trainer)
        if epoch == 0 or (epoch + 1) % 20 == 0:
            animator.add(epoch + 1, (evaluate_loss(net, train_iter, loss),
                                     evaluate_loss(net, test_iter, loss)))
    print('вес:', net.get_weights()[0].T)

PaddlePaddle:

def train(train_features, test_features, train_labels, test_labels,
          num_epochs=400):
    loss = nn.MSELoss()
    input_shape = train_features.shape[-1]
    # Смещение не задаём, так как оно уже реализовано в полиномиальных признаках
    net = nn.Sequential(nn.Linear(input_shape, 1, bias_attr=False))
    batch_size = min(10, train_labels.shape[0])
    print(batch_size)
    train_iter = d2l.load_array(((train_features, train_labels.reshape([-1,1]))),
                                batch_size)
    test_iter = d2l.load_array((test_features, test_labels.reshape([-1,1])),
                               batch_size, is_train=False)
    trainer = paddle.optimizer.SGD(parameters=net.parameters(), learning_rate=0.01)
    animator = d2l.Animator(xlabel='эпоха', ylabel='потери', yscale='log',
                            xlim=[1, num_epochs], ylim=[1e-3, 1e2],
                            legend=['обучение', 'тест'])
    for epoch in range(num_epochs):
        d2l.train_epoch_ch3(net, train_iter, loss, trainer)
        if epoch == 0 or (epoch + 1) % 20 == 0:
            animator.add(epoch + 1, (evaluate_loss(net, train_iter, loss),
                                     evaluate_loss(net, test_iter, loss)))
    print('вес:', net[0].weight.numpy())

4.4.4.3 Подгонка многочленом третьей степени (нормальный случай)

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

# Из полиномиальных признаков выбираем первые 4 измерения, то есть 1, x, x^2/2!, x^3/3!
train(poly_features[:n_train, :4], poly_features[n_train:, :4],
      labels[:n_train], labels[n_train:])

4.4.4.4 Линейная подгонка (недообучение)

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

# Из полиномиальных признаков выбираем первые 2 измерения, то есть 1 и x
train(poly_features[:n_train, :2], poly_features[n_train:, :2],
      labels[:n_train], labels[n_train:])

4.4.4.5 Подгонка многочленом высокой степени (переобучение)

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

# Берём все измерения полиномиальных признаков
train(poly_features[:n_train, :], poly_features[n_train:, :],
      labels[:n_train], labels[n_train:], num_epochs=1500)

В следующих разделах мы продолжим обсуждать проблему переобучения и методы борьбы с ней, например затухание весов и исключение (dropout).

Резюме

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

Упражнения

  1. Можно ли точно решить эту задачу полиномиальной регрессии? Подсказка: используйте линейную алгебру.
  2. Рассмотрите выбор модели для многочленов.
    1. Постройте график зависимости ошибки обучения от сложности модели (степени многочлена). Что вы наблюдаете? Многочлен какой степени нужен, чтобы уменьшить ошибку обучения до 0?
    2. Постройте график ошибки на тесте в этом случае.
    3. Постройте такой же график в зависимости от объёма данных.
  3. Что произойдёт, если не нормировать полиномиальные признаки ()? Можно ли решить эту проблему другим способом?
  4. Может ли ошибка обобщения быть равна нулю?

MXNet: Обсуждение

PyTorch: Обсуждение

TensorFlow: Обсуждение

PaddlePaddle: Обсуждение