- PVSM.RU - https://www.pvsm.ru -
Впервые идея GAN была опубликована Яном Гудфеллоу Generative Adversarial Nets, Goodfellow et alб 2014 [1], после этого GAN'ы являются одними из лучших генеративнх моделей.
Как и у любой другой генеративной модели задача GAN построить модель данных, а если более конкретно научиться генерировать семплы из распределения максимально близкого к распределению данных (обычно имеется датасет ограниченного размера, распределение данных в котором мы хотим промоделировать).
GAN’ы огромным количеством достоинств, но у них есть один существенный недостаток – их очень сложно обучать.
В последнее время вышел ряд работ посвященных устойчивости GAN:
Вдохновившись их идеями, я сделал небольшое свое исследование.
Я пытался сделать текст максимально простым и по-возможности использовать только простейшую математику. К сожалению, для того чтобы обосновать почему мы можем рассматривать свойства 2-х мерных векторных полей, приходится немножко копнуть в сторону вариационного исчисления. Но если кому-то эти термины не знакомы – можете смело переходить сразу к рассмотрению 2-х мерных векторных полей для разных типов GAN.
Мы попробуем сейчас заглянуть под капот процедуры обучения и понять чтоже там происходит.
GAN'ы состоят из двух нейронных сетей: дискриминатора и генератора. Генератор — позволяет семплировать из некоторого распределения (обычно его называют распределением генератора). Дискриминатор получает на вход семплы из оригинального датасета и генератора и учится предсказывать откуда этот семпл (датасет или генератор).
Схема GAN:

Процесс обучения GAN выглядит следующим образом:
Мы не будем рассматривать детально процесс обучения GAN. В интернете, да и на хабре в частности, имеется множество статей объясняющих этот процесс в деталях.
Нас будет интересовать совсем другое. А именно, из-за того что у нас идет соревнование двух нейронных сетей, задача перестает быть поиском минимума (максимума), а превращается в частных случаях в поиск седловой точки (т.е на 2 и 3 шаге мы один и тот же функционал пытаемся максимизировать по параметрам дискриминатора и минимизировать по параметрам генератора), а в более общих на 2 и 3 шаге мы можем оптимизировать абсолютно разные функционалы. Очевидно что задача минимакса это частный случай оптимизации разных функционалов – один функционал берется с разными знаками.
Давайте посмотрим на это в формулах. Будем считать что pd(x) – распределение откуда семплирован датасет, pg(x) – это распределение генератора, D(x) – выход с дискриминатора.
При обучении дискриминатора мы зачастую максимизируем такой функционал:
$inline$J = int{p_d(x)log(D(x))dx + int{p_g(x)log(1 - D(x))dx}}$inline$
Вектор градиентов:
$inline$v= nabla_theta J =int{frac{p_d(x)}{D(x)}nabla_theta D(x) dx + int{frac{p_g(x)}{1 - D(x)}nabla_theta D(x)dx}}$inline$
При обучении генератора мы максимизируем:
$inline$I = -int{p_g(x)log(1 - D(x))dx}$inline$
Вектор градиентов при этом:
$inline$u = nabla_varphi I = -int{{nabla_varphi p}_g(x)log(1 - D(x))dx}$inline$
В дальнейшем мы увидим что можно функционалы заменить соответственно на:
$inline$J = int{p_d(x)f_1(D(x))dx + int{p_g(x)f_2(D(x))dx}}$inline$
$inline$I = int{p_g(x)f_3(D(x))dx}$inline$
Где $inline$f_1, f_2, f_3$inline$ выбираются по определенным правилам. Кстати Ян Гудфеллоу в своей оригинальной статье использует $inline$f_1 и f_2$inline$ как при обучении обычного дискриминатора, а $inline$f_3$inline$ выбирает таким чтобы улучшить градиенты на начальном этапе обучения:
$inline$f_1left(xright)=logleft(xright), f_2left(xright)=logleft(1 - xright),f_3left(xright)=logleft(xright)$inline$
На первый взгляд казалось бы задача очень похожа на обычную задачу обучения градиентным спуском (подъемом). Почему же тогда все кто сталкивался с обучением GAN’ов сходятся в том что это чертовски трудно?
Ответ кроется в структуре векторного поля, которое мы используем для обновления параметров нейронных сетей. В случае обычной задачи классификации мы используем только вектор градиентов, т.е поле является потенциальным (собственно оптимизируемый функционал является потенциалом этого векторного поля). А потенциальные векторные поля обладают некоторыми замечательными свойствами, одним из которых является отсутствие замкнутых кривых. Т.е в этом поле невозможно ходить кругами. А вот при обучении GAN несмотря на то что векторные поля для генератора и дискриминатора по отдельности являются потенциальными (это же градиетны), суммарное векторное поле не будет потенциальным. А это означает, что в этом поле могут быть замкнутые кривые, т.е мы можем ходить кругами. А это очень и очень плохо.
Возникает вопрос почему же все таки нам удается достаточно успешно обучать GAN, может поле таки безвихревое (потенциальное)? А если это так, то почему это так сложно?
Забегу вперед, поле к сожалению не является потенциальным, но оно обладает рядом хороших свойств. К сожалению, поле так же очень чувствительно к параметризации нейронных сетей (выбору функций активации, использованию DropOut, BatchNormalization и т.д.). Но обо всем по-порядку.
Мы будем рассматривать функционалы обучения GAN в самом общем виде:
$inline$J = int{p_d(x)f_1(D(x))dx + int{p_g(x)f_2(D(x))dx}}$inline$
$inline$I = int{p_g(x)f_3(D(x))dx}$inline$
Нам необходимо оптимизировать оба функционала одновременно. Если предположить что D(x) и pg(x) абсолютно гибкие функции, т.е. мы можем в любой точке брать любое число, независимо от других точек. То известный факт из вариационного исчисления – изменять функцию нужно в напралении вариационной производной этого функционала (в общем-то полный аналог градиентного подъема).
Выпишем вариационную производную:
$inline$frac{partial J}{partial D(x)}=p_d(x)f_1'(D(x)) + p_g(x)f_2'(D(x))$inline$
$inline$frac{partial I}{partial p_g(x)}=f_3(D(x))$inline$
Мы будем рассматривать только первый фукнционал (для дискриминатора), для второго будет все тоже самое.
Но учитывая что на самом деле функцию мы можем менять только в множестве функций которые предствимы нашей нейронной сетью мы запишем:
$inline$∆D(x) = frac{partial D(x)}{partial θ_j}∆θ_j$inline$
изменения параметров сети, в общем то обычный градиентный спуск (подъем):
$inline$∆θj=frac{partial J}{partial θ_j}μ$inline$
µ — скорость обучения. Ну и производная по параметрам сети:
$inline$frac{partial J}{partialtheta_j}=int{frac{partial J}{partial D(y)}frac{partial D(y)}{partialtheta_j}dy}$inline$
А теперь собираем все вместе:
$inline$∆D(x) = sum_{j}{frac{partial D(x)}{partialtheta_j}int{frac{partial J}{partial D(y)}frac{partial D(y)}{partialtheta_j}dy}mu =muintfrac{partial J}{partial D(y)}}sum_{j}{frac{partial D(x)}{partialtheta_j}frac{partial D(x)}{partialtheta_j}dy = }muint{frac{partial J}{partial D(y)}K_theta(x,y)dy}$inline$
Где: $inline$K_theta(x,y) =sum_{j}{frac{partial D(x)}{partialtheta_j}frac{partial D(x)}{partialtheta_j} }$inline$
Я раньше не встречал этой функции в литературе по машинному обучению, поэтому назову ее параметрическим ядром системы.
Ну или если перейти к непрерывным шагам по времи (от разностных уравнений к дифференциальным) получим:
$inline$frac{d}{dt}D(x) = int{frac{partial J}{partial D(y)}K_theta(x,y)dy}$inline$
Это уравнение показывает внутреннюю связь оригинального поля (поточечного для дискриминатора) и параметризации нейронной сети. Полностью аналогичное уравнение мы получим для генератора.
Учитывая что K(x,y) (параметрическое ядро) это положительно определенная функция (ну а как же ведь она представима в виде скалярного произведения градиентов в соответсвующих точках), можно сделать вывод что любые изменения обучаемых функций (дискриминатора и генератора) принадлежат гильбертову пространству порожденному ядром, т.е. K(x,y). Интересно можно ли здесь получить какие нибудь содержательные результаты? Но мы в ту сторону смотреть пока не будем, а посмотрим в другую.
Как видно устойчивость GAN определяется двумя составляющими: вариационными производными функционалов и параметризацией нейронной сети. Наша задача посмотреть как ведет себя это поле поточечно, т.е в случае если наша сеть может представить абсолютно любую функцию. Задача превращается в анализ двумерного векторного поля. А это, я думаю, в наших силах.
Итак, мы рассматриваем следующее векторное поле:
$inline$frac{d}{dt}D(x)= frac{partial J}{partial D(x)}$inline$
$inline$frac{d}{dt}p_g(x)= frac{partial I}{partial p_g(x)}$inline$
Очевидно что эти уравнения можно рассматривать всего лишь для одной точки х, с учетом того как выглядят наши вариационные производные:
$inline$frac{d}{dt}D= p_df_1^prime(D) + p_gf_2^prime(D)$inline$
$inline$frac{d}{dt}p_g = f_3(D)$inline$
Первое требование к этой системе уравнений – правые части должны обращаться в 0 когда:
$inline$p_d=p_g$inline$
Иначе мы будем пытаться обучать модель, которая заведомо не будет сходится к правильному решению. Т.е. D обязано быть решением следующего уравннеия:
$inline$f_1^prime(D) + f_2^prime(D) = 0$inline$
Обозначим это решение как $inline$D0$inline$.
С учетом того что pg(x) плотность вероятности к правой части мы можем прибавить любое число не нарушая производных. Для того чтобы обеспечить 0 правой части в нужной точке вычтем значение в т. $inline$D0$inline$ (это необходимо делать, если мы хотим рассматривать pg поточечно – переход от поля параметризованного плотностями вероятностей к свободным полям).
Как результат получаем следующее поле:
$inline$frac{d}{dt}D= p_df_1^prime(D) + p_gf_2^prime(D)$inline$
$inline$frac{d}{dt}p_g = f_3(D) - f(D0)$inline$
C этого момента будем изучать точки покоя и устойчивость полей именно такого вида.
Мы можем изучать два вида устойчивости: локальную (в окрестности точки покоя) и глобальную (используя метод функций Ляпунова).
Для изучения локальной устойчивости необходимо вычислить матрицу Якоби поля.
Для того чтобы поле было локально “устойчивым” необходимо чтобы собственные числа имели отрицательную действительную часть.
В классическом GAN мы используем обычный logloss:
$inline$J = int{p_d(x)log(D(x))dx + int{p_g(x)log(1 - D(x))dx}}$inline$
Для обучения дискриминатора необходимо его максимизировать, для генератора – минимизировать. При этом поле будет выглядеть так:
$inline$frac{d}{dt}D= frac{p_d}{D} - frac{p_g}{log(1-D)}$inline$
$inline$frac{d}{dt}p_g = -log(1-D) + log(frac{1}{2})$inline$
Давайте посмотрим как будут эволюционировать параметры (pg и D) в этом поле. Для этого используем такой простой питоновский скрипт:
def get_v(d, pg, pd):
vd = pd/d - pg/(1.-d)
vpg = -np.log(1.-d) + np.log(0.5)
return vd, vpg
d = 0.75
pg = 0.9
pd = 0.2
d_hist = []
pg_hist = []
lr = 1e-3
n_iter = 100000
for i in range(n_iter):
d_hist.append(d)
pg_hist.append(pg)
vd, vpg = get_v(d, pg, pd)
d = d + lr*vd
pg = pg + lr*vpg
plt.plot(d_hist, pg_hist, '-')
plt.show()
Для начальной точки $inline$p_g = 0.9, D = 0.25$inline$ это будет выглядеть так:

Точка покоя такого поля будет: pg = pd и D = 0.5
Можно легко проверить что действительные части собственных чисел матрицы Якоби отрицательны, т.е поле локально устойчиво.
Мы не будем заниматься доказательством глобальной устойчивости. Но если очень интересно можно поиграться с питоновским скриптом и убедиться что поле устойчиво для любых допустимых начальных значений.
Мы уже обсуждали выше что Ян Гудфеллоу в оригинальной статье использовал немного модифицированную версию GAN. Для его версии функции были такими:
$inline$f_1left(xright)=logleft(xright), f_2left(xright)=logleft(1 - xright),f_3left(xright)=logleft(xright)$inline$
Поле же будет выглядеть так:
$inline$frac{d}{dt}D= frac{p_d}{D} - frac{p_g}{log(1-D)}$inline$
$inline$frac{d}{dt}p_g = log(D) - log(frac{1}{2})$inline$
Питоновский скрипт будет тот же, только отлична функция поля:
def get_v(d, pg, pd):
vd = pd/d - pg/(1.-d)
vpg = np.log(d) - np.log(0.5)
return vd, vpg
d = 0.75
pg = 0.9
pd = 0.2
d_hist = []
pg_hist = []
lr = 1e-3
n_iter = 100000
for i in range(n_iter):
d_hist.append(d)
pg_hist.append(pg)
vd, vpg = get_v(d, pg, pd)
d = d + lr*vd
pg = pg + lr*vpg
plt.plot(d_hist, pg_hist, '-')
plt.show()
И при тех же начальных данных картинка выглядит так:

И опять же легко проверить что поле будет локально устойчивым.
Т.е с точки зрения сходимости такая модификация не ухудшает свойства GAN, зато имеет свои преимущества с точки зрения обучения нейронных сетей.
Давайте посмотрим на еще один популярный вид GAN. Оптимизируемый функционал выглядит так:
$inline$J = int{p_d(x)D(x)dx - }int{p_g(x)D(x)dx}$inline$
Где D принадлежит классу 1-Липшицевых функций по х.
Мы хотим максимизировать его по D и минимизировать по pg.
Очевидно что в этом случае: $inline$f_1left(xright)=x, f_2left(xright)=-x, f_3left(xright)=x$inline$
И поле будет выглядеть так:
$inline$frac{d}{dt}D= p_d -{ p}_g $inline$
$inline$frac{d}{dt}p_g = D$inline$
В этом поле легко угадывается окружность с центром в точке $inline$p_g = p_d, D = 0$inline$.
Т.е если мы пойдем по этому полю, то будем вечно ходить по кругу.
Вот пример траектории в таком поле:

Возникает вопрос, почему же тогда получается обучать этот вид GAN? Ответ очень прост – в данном анализе не учитывается факт 1-Липшицевости D. Т.е мы не можем брать произвольные функции. Кстати это хорошо согласуется с результатами авторов… статьи. Для избежания хождения по кругу они рекомндуют тренировать дискриминатор до сходимости: Wasserstein GAN [4]
Подбором функций $inline$f_1, f_2 и f_3$inline$ можно создавать различные варианты GAN. Главное требование к этим функциям это обеспечить наличие «правильной» точки покоя и устойчивость этой точки (желательно глобальную, но хотя бы локальную). Предоставляю читателю возможность самому вывести ограничения на функции f1, f2 и f3, необходимые для локальной устойчивости. Это несложно – достаточно рассмотреть квадратное уравнение для собственных чисел матрицы Якоби.
Приведу пример такого GAN:
$inline$f_1(x) = -0.5x^2, f_2(x) = x, f_3(x) = -x$inline$
Опять же предлагаю читателю самому построить поле этого GAN и доказать его устойчивость. (Кстати это одно из немногих полей для которого доказательтво глобальной устойчивости элементарно – достаточно выбрать функцию Ляпунова расстояние до точки покоя). Только нужно учесть что точка покоя D = 1.
Из приведенного анализа видно что все классические GAN (за исключением Wassertein GAN, которая имеет свои способы улучшения стабильности) обладают «хорошими» полями. Т.е. следование этим полям имеет единственную точку покая в которой распределение генератора равно распределению данных.
Почему же тогда обучение GAN такая тяжелая задача. Ответ прост – параметризация нейронных сетей. При «плохой» параметризации мы можем так же пойти гулять кругами. Например в мои эксперименты показывают что, например, использование BatchNormalization в любой из сетей сразу превращает поле в замкнутое. А лучше всего работает Relu активация.
К сожалению на данный момент нет ни единого способа теоретически проверить какие элементы нейросети как меняют поле. Мне докажется переспективным исследовать свойства парметрического ядра системы — $inline$K_theta(x,y)$inline$.
Хотелось еще рассказать про способы регуляризации полей GAN и взгляд с позиции двумерных полей на это. Рассмотреть алгоритмы Reinforcement Learning с этой позиции. И еще много чего. Но к сожалению, статья и так получилась слишком большой, так что об этом как нибудь в другой раз.
Автор: Yurec666
Источник [5]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/matematika/285446
Ссылки в тексте:
[1] Generative Adversarial Nets, Goodfellow et alб 2014: https://arxiv.org/abs/1406.2661
[2] Gradient descent GAN optimization is locally stable, Vaishnavh Nagarajan, J. Zico Kolter, 2017: https://arxiv.org/abs/1706.04156
[3] The Numerics of GANs, Lars Mescheder et al, 2017: https://arxiv.org/abs/1705.10461
[4] Wasserstein GAN: https://arxiv.org/abs/1701.07875
[5] Источник: https://habr.com/post/416531/?utm_source=habrahabr&utm_medium=rss&utm_campaign=416531
Нажмите здесь для печати.