Содержание страницы
GAN – основные принципы
Здравствуйте и вновь добро пожаловать на занятия по теме «Глубокое обучение без учителя, часть 2».
В этой части курса мы переходим к новой теме – GAN. Мы подробно разберем GAN, функции затрат GAN и под конец рассмотрим DCGAN.
GAN расшифровывается как генеративно-состязательная сеть (generative adversarial network), и по мере прохождения части станет ясно, почему она имеет такое название. GAN были названы наиболее интересной разработкой 2016 года, хотя на самом деле они изобретены в 2014-м. Основная привлекательность GAN заключается в том, что они чрезвычайно хорошо подходят для создания реалистичных, правдоподобных образцов. Как вы видели ранее в курсе, GAN используются для создания очень интересных приложений, таких как преобразование набросков в изображения фотографического качества, и именно поэтому исследователи в области глубокого обучения возлагают на GAN большие надежды.
В конкретно этой лекции мы познакомимся с базовыми принципами, лежащими в основе работы GAN. Начнём с обсуждения в общих чертах. Что общего есть у до сих пор обсуждавшихся моделей? Напомню, что мы рассматривали байесовский классификатор и вариационный автокодировщик. В обоих этих моделях мы очень много работали с плотностями распределения вероятностей. В случае же GAN мы не будем работать с явными плотностями распределений вероятностей вовсе. Вы увидите, что вместо этого нашей целью при обучении GAN будет достижение равновесия по Нэшу.
Можно привести ещё одно сравнение. В предыдущем курсе по глубокому обучению без учителя мы рассматривали ограниченные машины Больцмана. Они также в состоянии генерировать образцы, но требуют выборки по методу Монте-Карло, что, в свою очередь, требует тысяч итераций, а их недостатком является то, что нет способа определить, сколько нужно итераций. С другой же стороны, GAN могут генерировать образцы за один проход.
Ещё одна тема, которую нужно обсудить в общих чертах, – это объективное качество. Как правило, когда мы работаем с моделями машинного обучения, у нас есть некоторая функция ошибок вроде отрицательного значения логарифма правдоподобия, которую нужно оптимизировать; мы можем сказать, насколько хороша модель, рассматривая её ошибки. В случае обучения с учителем это особенно хорошо видно, так как мы можем рассматривать точность или ошибку R2 – эти вещи являются объективными. Однако оценка качества выборки очень субъективна: не существует количественной оценки того, насколько правдоподобно выглядит образец. Мы знаем, что GAN создают лучше выглядящие образцы, нежели вариационные автокодировщики, но у нас нет числа, которое бы это показало. Мы знаем об этом, лишь рассматривая изображения, которые они создают. Таким образом, нужно свыкнуться с мыслью, что не существует числа, которое бы отображало качественность, – мы должны использовать наши органы чувств.
На этом месте мы переходим к тому, чтобы поговорить о том, как работают GAN. И вновь-таки, на самом деле это совокупность двух различных нейронных сетей. Одна из них называется генератором, а вторая – дискриминатором. Суть в том, что эти две нейронные сети буду вести друг с другом дуэль. Задача генератора – попытаться «обмануть» дискриминатора; выполняет он её путём генерации очень реалистично выглядящих образцов. Задача дискриминатора, как можно понять по его названию, – провести классификацию между образцами, созданными генератором, и реальными изображениями.
Одна из аналогий, использующихся для объяснения работы с GAN, состоит в следующем. Генератор – это нечто вроде покупателя, пытающегося расплатиться фальшивыми купюрами. Дискриминатор же является владельцем магазина, который обязан принять настоящие купюры, но отклонить фальшивые. Вначале генератор приносит чёрно-белую ксерокопию или самодельные рисунки, которые, разумеется, не сильно похожи на оригинал, так что они отклоняются дискриминатором. Но генератор делает из этого выводы и в следующий раз использует цветную ксерокопию. Однако она по-прежнему более похожа на деньги из игры «Монополия», так что дискриминатор обучается определять и эти подделки. Тогда генератор выясняет, какая бумага используется для печати настоящих купюр, и начинает печатать свои на этой бумаге и так до бесконечности.
Как видите, на самом деле это довольно простая концепция. В действительности она, опять же, не содержит никаких компонент, с которыми мы бы не были знакомы: мы знаем, как создавать нейронные сети, просто теперь нам нужно создать их две; мы знаем, как создавать целевые функции, просто теперь нам нужно создать одну из них противоположной другой и так далее. Так что в целом всё выглядит довольно просто. На самом деле можно попробовать сделать прототип, используя лишь приведенную информацию. Но, конечно, мы воспользуемся плодами упорного труда исследователей в области глубокого обучения, чтобы убедиться, что всё идёт гладко.
Функция затрат GAN (часть 1)
Мы подробнее разберём целевую функцию генеративно-состязательной сети. У нас есть две нейронные сети.
Нужны ли нам две функции затрат? Или достаточно одной, но которую одновременно одна нейронная сеть старается максимизировать, а вторая – минимизировать? Эта лекция целиком посвящена чёткому прояснению данного вопроса.
Начнём с той общей идеи, что две наши нейронные сети стараются оптимизировать противоположные вещи (что конкретно значит «противоположные», мы увидим через минуту). Начнём же с дискриминатора, поскольку он выполняет классификацию, а мы знаем, что обучение с учителем немного проще. Какого рода классификацию должен выполнять дискриминатор? Выполняет он её, получая два вида изображений – настоящие и фальшивые; это просто две разные метки, а значит, мы выполняем двоичную классификацию. А какая функция затрат подходит для двоичной классификации? Мы её уже на самом деле видели в разделе, посвящённом вариационным автокодировщикам, – это двоичная кросс-энтропийная функция. Вспомним, как она выглядит:
где t представляет собой целевую переменную, а y – исходящую вероятность дискриминатора.
Предположим, что t = 1 означает настоящее изображение, а t = 0 – фальшивое. Тогда можно утверждать, что y = D(x), являющееся числом между 0 и 1, представляет собой вероятность того, что изображение x является настоящим. Это общепринятый способ обозначения дискриминатора – просто D(x). Для завершенности предположим, что параметрами дискриминатора является θD, так что мы также можем записать D(x; θD). В этом случае x может представлять как реальное, так и фальшивое изображение.
Но мы также можем использовать обозначение x только для настоящих изображений и – только для фальшивых. Тогда можно упростить функцию затрат, полностью убрав t:
Перейдём теперь к генератору. Можем ли мы и его представить в виде функции? Как вы можете догадаться, логично обозначить функцию генератора буквой G, а точнее, функцией G(z). Здесь z представляет скрытую априорную вероятность, так что мы имеем такого же рода графическую структуру, как и в случае вариационного автокодировщика. Действительно, произведение выборки будет происходить очень схожим образом: вначале мы выбираем z из априорной вероятности, а затем подставляем в G, чтобы получить окончательный образец изображения. И конечно же, параметры G мы обозначаем через θG: G(z; θG).
Теперь имея выражения и для дискриминатора, и для генератора, мы можем переписать кросс-энтропийную функцию в чуть другом виде, а именно:
Тут уже x представляет собой настоящее изображение, тогда как G(z) – фальшивое. Нам больше не нужна целевая переменная, ведь мы и так знаем, что все x тут – настоящие, а все G(z) – фальшивые.
Но в действительности мы будем рассматривать пакеты данных, поэтому можем переписать функцию затрат с точки зрения суммы функций затрат для каждой учебной выборки, и эта сумма, конечно же, является лишь отрицательным логарифмом правдоподобия по всем данным в пакете:
Однако на самом деле это выражение представляет ожидаемое значение, которое в действительности мы и хотим оптимизировать, так что иногда вы можете встретить функцию затрат, выраженную следующим образом:
Это всё, что касается функции затрат дискриминатора.
Но выполнены должны быть две оптимизации. Во-первых, дискриминатор хочет максимизировать свою способность различать настоящие и фальшивые изображения, что минимизирует двоичную кросс-энтропийную функцию. Во-вторых, генератор желает максимизировать свою способность обманывать дискриминатор. Как же это сделать? Ну, мы можем просто максимизировать ту же функцию ошибок. Другими словами, можно просто сказать, что функция затрат генератора равна отрицательному значению функции затрат дискриминатора:
В теории игр это называется игрой с нулевой суммой, поскольку сумма потерь всех игроков всегда равна нулю. Разумеется, чтобы понять этот курс, вам не нужно никаких познаний в теории игр, и знание, что это называется «игрой с нулевой суммой», нам особо не поможет, просто может оказаться любопытным, если вы знакомы с теорией игр.
Если вы знакомы с теорией игр, то вам может быть интересно, что игры с нулевой суммой также называются минимаксными, поскольку решение включает в себя нахождение и минимумов, и максимумов. Конкретнее говоря, мы минимизируем относительно θD, но максимизируем относительно θG, или, посмотрев с другой стороны, максимизируем –JD относительно θD и минимизируем относительно θG:
В теории игр это называется игрой с нулевой суммой, поскольку сумма потерь всех игроков всегда равна нулю. Разумеется, чтобы понять этот курс, вам не нужно никаких познаний в теории игр, и знание, что это называется «игрой с нулевой суммой», нам особо не поможет, просто может оказаться любопытным, если вы знакомы с теорией игр.
Если вы знакомы с теорией игр, то вам может быть интересно, что игры с нулевой суммой также называются минимаксными, поскольку решение включает в себя нахождение и минимумов, и максимумов. Конкретнее говоря, мы минимизируем относительно θD, но максимизируем относительно θG, или, посмотрев с другой стороны, максимизируем –JD относительно θD и минимизируем относительно θG:
Теоретический анализ, выполненный в контексте теории игр, не имеет особого отношения к данному курсу, но если вам интересно, то можете ознакомиться со статьёй в файле extra_reading.txt.
Что дальше? Имея функцию затрат, мы можем начать минимизировать её, воспользовавшись одним из оптимизаторов градиентного спуска библиотеки Tensorflow. Однако у нас получается интересная ситуация, с которой мы ещё не сталкивались: у нас есть две нейронные сети, а значит, нужно создать два разных оптимизатора. Если не учитывать этого, алгоритм относительно прост. В цикле мы вначале получаем набор реальных образцов и ряд фальшивых, созданных генератором. Второй этап – выполнить одну итерацию градиентного спуска для дискриминатора. Третий – запустить одну итерацию градиентного спуска для генератора, и повторять всё это до тех пор, пока не начнём наблюдать хорошие образцы. В некоторых практических реализациях градиентный спуск для генератора выполняется дважды, и сообщают о лучших результатах. Это можно изменять в зависимости от того, как работает GAN: если дискриминатор начинает работать слишком хорошо, можно чаще обучать генератор.
Функция затрат GAN (часть 2)
Обычно в этот момент мы сразу переходим от псевдокода к коду, но в случае с GAN остаются некоторые важные аспекты, которые необходимо рассмотреть. Это определённо не кажется очевидным, однако с только что определённой функцией затрат остаются некоторые проблемы. Эта лекция полностью посвящена раскрытию этих проблем и поиску их решения.
В чём же проблема с функцией затрат как она есть? Проблема с ней в возникает с точки зрения генератора. Рассмотрим кросс-энтропийную функцию подробнее:
Мы видим, что она состоит из двух членов: первый член соответствует настоящим изображениям, а второй – фальшивым, создаваемым генератором. Градиент первого члена относительно параметров генератора равен нулю, потому что параметры генератора там нигде не появляются. Таким образом, единственной стоящей рассмотрения частью, с точки зрения генератора, является второй член.
Предположим, дискриминатор очень хорошо справляется с классификацией настоящих и фальшивых изображений. Это значит, что D(G(z)) будет очень близко к нулю, а значит – и весь второй член будет равен нулю. Однако нас интересует градиент, поэтому рассмотрим наклон кривой. Что представляет из себя наклон в районе нулю? По сути, это соответствует наименьшему возможному градиенту среди всех возможных. И это проблема, поскольку градиентный спуск является нашим обучающим алгоритмом – мы всегда двигаемся в направлении, пропорциональном градиенту. Другими словами, когда дискриминатор очень хорош в классификации настоящих и фальшивых изображений, то у генератора остаётся мало шансов улучшить свою работу.
Каково же решение? Мы можем просто использовать другую функцию затрат для генератора. Основная идея – «перевернуть цель», другими словами, вместо того, чтобы стремиться к целевой переменной, равной нулю для фальшивых изображений, генератор стремится к целевой переменной, равной 1. Иначе говоря, мы будем стараться минимизировать отрицательное ожидаемое значение логарифма D(G(z)):
Это логично, поскольку логарифм данного отдельного члена соответствует целевой переменной, равной 1, в двоичной кросс-энтропийной функции, а, как обсуждалось ранее, другой член не имеет значения, поскольку его градиент относительно параметров генератора равен нулю.
Одно замечание: поскольку эта новая функция затрат означает, что сумма функций затрат дискриминатора и генератора больше не равна нулю, то это больше не игра с нулевой суммой. Эту функцию затрат называют ненасыщенной эвристикой. Что это значит? Когда мы говорим «ненасыщенная», это значит, что градиент не насыщен, то есть не сходится к некоторому единичному значению. В нашем случае мы видим, что этого не происходит. Когда генератор ошибается, градиент становится круче, и чем больше ошибается – тем более крутым становится. Это хорошо, поскольку чем больше ошибается генератор, тем больший шаг он делает для исправления своих ошибок. Это и называется эвристикой, поскольку мы делаем это, лишь чтобы решить численное затруднение. Это противоположно теоретическому выводу функции затрат. Таким образом, нет никакого теоретического обоснования такой функции затрат, просто это удобно, потому что решает численную проблему.
Важно отметить, что если вы заглянете в одно из руководств, написанных изобретателями генеративно-состязательных сетей, то заметите, что некоторые из утверждений и диаграмм в этой работе в некотором роде вводят в заблуждение. В частности, можно видеть, что график, показывающий функцию затрат относительно D(G(z)) масштабирован очень странным способом. Мы, правда, в этой лекции используем такой же, но это лишь для большей последовательности. Как вы можете видеть, он очень сильно сжат в вертикальном направлении; другими словами, вертикальная шкала очень большая, тогда как горизонтальная изменяется лишь от 0 до 1. Это вызывает ощущение, будто наклон плоский, тогда как на самом деле – нет. Если взять производную от log (1 – x), то получится -1/(1 – x). Для этой производной если x = 0, то наклон равен -1, а если x = 1, то наклон будет равен минус бесконечности. То есть на самом деле наименьшим наклоном будет -1, а не 0. В статье указывается, что градиент исчезает, что звучит так, будто градиент стремится к нулю, а в сочетании с графиком, выглядящим так, будет наклон стремится к нулю, можно действительно подумать, что он стремится к нулю. Важно знать, что это только потому, что график отмасштабирован таким образом. На самом же деле наклон не стремится к нулю.
Теперь, зная функции затрат как для дискриминатора, так и для генератора, мы можем взглянуть на блок-схему, которая обрисовывает происходящее. Стартуя с верхнего левого угла, мы видим, что начинаем со скрытой переменной z, которая генерируется из равномерного распределения. Она передаётся в генератор, являющийся нейронной сетью, которая просто генерирует нечто, имеющее такую же форму, что и учебные данные, и обозначается через