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

11.1 Оптимизация и глубокое обучение

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

11.1.1 Цели оптимизации

Хотя оптимизация даёт способ минимизировать функцию потерь в глубоком обучении, по сути цели оптимизации и глубокого обучения принципиально различаются. Оптимизация в первую очередь занимается минимизацией целевой функции, тогда как глубокое обучение стремится найти подходящую модель при ограниченном объёме данных. В разделе 4.4 мы подробно обсуждали различие между этими целями. Например, ошибка на обучении и ошибка обобщения обычно различаются: поскольку целевая функция алгоритма оптимизации обычно основана на функции потерь на обучающем наборе данных, цель оптимизации — уменьшить ошибку на обучении. Однако цель глубокого обучения (или статистического вывода в более широком смысле) — уменьшить ошибку обобщения. Чтобы достичь второй цели, помимо алгоритмов оптимизации, которые уменьшают ошибку на обучении, нам нужно следить за переобучением.

MXNet:

%matplotlib inline
from d2l import mxnet as d2l
from mpl_toolkits import mplot3d
from mxnet import np, npx
npx.set_np()

PyTorch:

%matplotlib inline
from d2l import torch as d2l
import numpy as np
from mpl_toolkits import mplot3d
import torch

TensorFlow:

%matplotlib inline
from d2l import tensorflow as d2l
import numpy as np
from mpl_toolkits import mplot3d
import tensorflow as tf

PaddlePaddle:

%matplotlib inline
from d2l import paddle as d2l
import warnings
warnings.filterwarnings("ignore")
import numpy as np
from mpl_toolkits import mplot3d
import paddle

Чтобы показать различие между этими целями, введём два понятия: риск и эмпирический риск. Как сказано в разделе 4.9.3.1, эмпирический риск — это среднее значение потерь на обучающем наборе данных, а риск — ожидаемые потери по всей генеральной совокупности. Ниже мы определяем две функции: функцию риска f и функцию эмпирического риска g. Предположим, что у нас есть только конечный объём обучающих данных. Поэтому g оказывается менее гладкой, чем f.

#@save
def f(x):
    return x * d2l.cos(np.pi * x)

def g(x):
    return f(x) + 0.2 * d2l.cos(5 * np.pi * x)

На рисунке ниже видно, что минимум эмпирического риска на обучающем наборе может отличаться от минимума риска (ошибки обобщения).

MXNet, PyTorch, TensorFlow:

def annotate(text, xy, xytext):  #@save
    d2l.plt.gca().annotate(text, xy=xy, xytext=xytext,
                           arrowprops=dict(arrowstyle='->'))

x = d2l.arange(0.5, 1.5, 0.01)
d2l.set_figsize((4.5, 2.5))
d2l.plot(x, [f(x), g(x)], 'x', 'risk')
annotate('min of\nempirical risk', (1.0, -1.2), (0.5, -1.1))
annotate('min of risk', (1.1, -1.05), (0.95, -0.5))

PaddlePaddle:

def annotate(text, xy, xytext):  #@save
    d2l.plt.gca().annotate(text, xy=xy, xytext=xytext,
                           arrowprops=dict(arrowstyle='->'))

x = d2l.arange(0.5, 1.5, 0.01, dtype='float32')
d2l.set_figsize((4.5, 2.5))
d2l.plot(x, [f(x), g(x)], 'x', 'risk')
annotate('min of\nempirical risk', (1.0, -1.2), (0.5, -1.1))
annotate('min of risk', (1.1, -1.05), (0.95, -0.5))

11.1.2 Трудности оптимизации в глубоком обучении

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

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

11.1.2.1 Локальные минимумы

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

Например, для функции

можно приближённо найти её локальный и глобальный минимумы.

MXNet, PyTorch, TensorFlow:

x = d2l.arange(-1.0, 2.0, 0.01)
d2l.plot(x, [f(x), ], 'x', 'f(x)')
annotate('local minimum', (-0.3, -0.25), (-0.77, -1.0))
annotate('global minimum', (1.1, -0.95), (0.6, 0.8))

PaddlePaddle:

x = d2l.arange(-1.0, 2.0, 0.01, dtype='float32')
d2l.plot(x, [f(x), ], 'x', 'f(x)')
annotate('local minimum', (-0.3, -0.25), (-0.77, -1.0))
annotate('global minimum', (1.1, -0.95), (0.6, 0.8))

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

11.1.2.2 Седловые точки

Помимо локальных минимумов, другой причиной затухания градиента являются седловые точки. Седловая точка (saddle point) — это точка, в которой все градиенты функции обращаются в нуль, но которая не является ни глобальным, ни локальным минимумом. Рассмотрим функцию . Её первая и вторая производные в точке равны нулю. В этой точке оптимизация может остановиться, хотя это не минимум.

MXNet, PyTorch, TensorFlow:

x = d2l.arange(-2.0, 2.0, 0.01)
d2l.plot(x, [x**3], 'x', 'f(x)')
annotate('saddle point', (0, -0.2), (-0.52, -5.0))

PaddlePaddle:

x = d2l.arange(-2.0, 2.0, 0.01, dtype='float32')
d2l.plot(x, [x**3], 'x', 'f(x)')
annotate('saddle point', (0, -0.2), (-0.52, -5.0))

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

MXNet:

x, y = d2l.meshgrid(
    d2l.linspace(-1.0, 1.0, 101), d2l.linspace(-1.0, 1.0, 101))
z = x**2 - y**2
ax = d2l.plt.figure().add_subplot(111, projection='3d')
ax.plot_wireframe(x.asnumpy(), y.asnumpy(), z.asnumpy(),
                  **{'rstride': 10, 'cstride': 10})
ax.plot([0], [0], [0], 'rx')
ticks = [-1, 0, 1]
d2l.plt.xticks(ticks)
d2l.plt.yticks(ticks)
ax.set_zticks(ticks)
d2l.plt.xlabel('x')
d2l.plt.ylabel('y');

PyTorch, TensorFlow, PaddlePaddle:

x, y = d2l.meshgrid(
    d2l.linspace(-1.0, 1.0, 101), d2l.linspace(-1.0, 1.0, 101))
z = x**2 - y**2

ax = d2l.plt.figure().add_subplot(111, projection='3d')
ax.plot_wireframe(x, y, z, **{'rstride': 10, 'cstride': 10})
ax.plot([0], [0], [0], 'rx')
ticks = [-1, 0, 1]
d2l.plt.xticks(ticks)
d2l.plt.yticks(ticks)
ax.set_zticks(ticks)
d2l.plt.xlabel('x')
d2l.plt.ylabel('y');

Будем считать, что вход функции — -мерный вектор, а выход — скаляр. Тогда её матрица Гессе (матрица вторых производных) будет иметь собственных значений (см. онлайн-приложение о спектральном разложении). Решение функции может быть локальным минимумом, локальным максимумом или седловой точкой в месте, где градиент функции равен нулю:

  • если все собственные значения матрицы Гессе в точке нулевого градиента положительны, то в этой точке функция имеет локальный минимум;
  • если все собственные значения матрицы Гессе в точке нулевого градиента отрицательны, то в этой точке функция имеет локальный максимум;
  • если в точке нулевого градиента матрица Гессе имеет как положительные, так и отрицательные собственные значения, то это седловая точка функции.

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

11.1.2.3 Затухание градиента

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

MXNet, PyTorch, TensorFlow:

x = d2l.arange(-2.0, 5.0, 0.01)
d2l.plot(x, [d2l.tanh(x)], 'x', 'f(x)')
annotate('vanishing gradient', (4, 1), (2, 0.0))

PaddlePaddle:

x = d2l.arange(-2.0, 5.0, 0.01, dtype='float32')
d2l.plot(x, [d2l.tanh(x)], 'x', 'f(x)')
annotate('vanishing gradient', (4, 1), (2, 0.0))

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

Резюме

  • Минимизация ошибки на обучении не гарантирует, что мы найдём наилучший набор параметров для минимизации ошибки обобщения.
  • У задачи оптимизации может быть много локальных минимумов.
  • У задачи может быть много седловых точек, поскольку задачи, как правило, не выпуклые.
  • Затухание градиента может привести к остановке оптимизации; перепараметризация обычно помогает. Удачная инициализация параметров тоже может быть полезной.

Упражнения

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

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

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

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

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