Пошаговое экспериментирование с DCGAN с визуализацией его результатов

Всем привет, давно прошло! Сегодня я хочу написать о моем результате изучения и экспериментов с другим методом глубокого обучения, которым является Generative Adversarial Network (GAN). Я изучал и узнал об этом недавно. Думаю, было бы неплохо, если бы я поделился своим экспериментом со всеми.

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

Эта статья будет посвящена руководству по созданию GAN с объяснением каждого шага (с исходным кодом). Он будет нацелен на всех, кто интересуется искусственным интеллектом, особенно тех, кто хочет попрактиковаться в использовании глубокого обучения. Он также нацелен на всех, кто хочет научиться использовать GAN впервые. Я напишу эту статью как можно проще, чтобы понять об этом. Я надеюсь, что читатели, прочитавшие эту статью, знают, как работает GAN в целом.

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

В конце этой статьи есть ссылка на GitHub, если вы хотите узнать о полном исходном коде. А пока я дам блокнот Python и ссылку на Colaboratory в репозитории.

Изображение 0 - это одно из созданных лиц аниме-персонажей, которые мы создадим, используя изображение, сформированное моделью. Первое и второе изображение слева создано с помощью GAN. Третий - это сложение первой и второй граней (вы можете назвать это слиянием первой и второй граней).

Контур

  1. Технология
  2. Вступление
  3. Краткое описание GAN
  4. Реализация
  5. Полученные результаты
  6. Урок выучен
  7. Заключение
  8. Послесловие
  9. Репозиторий
  10. Источники

Технологии и данные

  1. Python 3.7
  2. Совместная работа: бесплатная среда для ноутбуков Jupyter, не требующая настройки и работающая полностью в облаке. Есть GPU Tesla K80 или даже TPU! К сожалению, альфа-версия Tensorflow v2.0 все еще не поддерживает TPU на момент написания этой статьи. К сожалению, обучение DCGAN через TPU невозможно.
  3. Керас: библиотека Python для глубокого обучения.
  4. Данные взяты отсюда

Вступление

Одна из горячих тем в области глубокого обучения - это Generative Adversarial Network (GAN). Представленный Яном Гудфеллоу и др., Он может создавать что-то с нуля без присмотра. В компьютерном зрении. Есть много исследователей, изучающих и улучшающих его. Например, NVIDIA создала генератор реалистичных лиц с помощью GAN. Есть также некоторые исследования в музыкальной сфере по использованию GAN. Моя предыдущая статья, в которой рассказывается о создании музыки, также может быть написана с помощью GAN.

Исследователи разработали множество вариантов GAN. Одним из новейших (на момент написания этой статьи) является HoloGAN, который может генерировать трехмерное представление из естественных изображений. Если вы посмотрите, как это можно сделать, это действительно потрясающе. Фактически, эти расширенные GAN следуют основным принципам работы GAN. У каждой GAN есть два агента в качестве обучаемых, дискриминатор и генератор (мы углубимся в эти термины позже). Чтобы узнать больше о продвинутых методах GAN, нужно знать, как работает базовая GAN.

В этой статье основное внимание будет уделено реализации Deep Convolutional GAN ​​(DCGAN), одного из вариантов GAN, предложенных A. Radford et al.. По сути, это GAN со многими слоями свертки. Это одна из популярных нейронных сетей GAN. Мы построим архитектуру, отличную от предложенной в их статье. Хотя он и отличается, он все же дает хорошие результаты.

Одна из интересных особенностей GAN заключается в том, что он создает свои скрытые переменные (одномерный вектор любой длины), которые могут управляться линейной алгеброй. Пример на Изображение 0 является одним из примеров. Вектор первого лица (слева) добавляется к вектору второго лица. Затем получается третье лицо.

Это также дает интересное распределение данных. У каждой точки раздачи разные лица. Например, данные, центрированные на среднем значении -0,7, будут иметь лицо с желтыми волосами.

Начнем с краткого описания GAN.

Краткое описание О GAN

Итак, что такое GAN?

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

Есть некоторые характерные аспекты GAN, которая является генеративной моделью, а именно:

  • Узнает совместную вероятность P (x, y), где x - это входные данные, а y - выходные. Он будет делать вывод на основе P (x | y), учитывая вывод y, он сделает вывод x. Можно сказать, что y - это настоящие данные в GAN.
  • Когда модели передаются обучающие реальные данные y, она узнает характеристики реальных данных. Он узнает, определив реальные данные переменную представления скрытых функций. Чтобы упростить задачу, он изучает базовый конструктор изображений в реальных данных. Например, модель может узнать, что лица построены по цвету глаз и волос. Эти два будут одной из базовых, которые будут использоваться для создания граней. Изменяя свою переменную, он также может изменять сгенерированные лица. Например, увеличив переменную для глаз, глаза станут чернее. В противном случае его понижение приведет к обратному.
  • Он может построить распределение вероятностей, такое как нормальное распределение, которое можно использовать для исключения выброса. Поскольку выброс обычно очень редко встречается в распределении, он будет очень редко его генерировать. Таким образом, GAN хорошо работает с реальными данными, у которых есть выбросы.

Итак, как это работает?

GAN состоит из двух нейронных сетей: Дискриминатор и Генератор. GAN заставит эти две сети сражаться друг с другом на основе игрового фреймворка с нулевой суммой (теория игр). Это игра между этими агентами (сетями). Название состязательного в GAN происходит от этой концепции.

Генератор сгенерирует некоторые поддельные данные, а Дискриминатор определит пару данных, которые содержат поддельные данные, созданные Генератором, и данные, взятые из реальных данных. . Задача генератора в основном состоит в том, чтобы генерировать некоторые поддельные данные, аналогичные реальным, и обмануть Дискриминатор при определении того, какие данные являются настоящими, а какие - поддельными. Задача Дискриминатора - сделать его более умным при идентификации настоящих и поддельных данных. Каждый агент будет двигаться поочередно. Мы надеемся, что, сражаясь с этими агентами, они станут сильнее, особенно Генератор.

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

Хорошо, другими словами, Генератор будет имитировать реальные данные путем выборки распределения, которое изучено и предназначено для того же распределения, что и реальные данные. Он обучит свою нейронную сеть, которая сможет его генерировать. Принимая во внимание, что Дискриминатор обучит свою нейронную сеть контролируемым методам обнаружения поддельных и реальных данных. Каждая сеть будет обучать свою сеть поочередно.

Вот приблизительные шаги по работе GAN:

  1. Сгенерируйте случайный шум в распределении вероятностей, таком как нормальное распределение.
  2. Сделайте это как вход для нашей нейронной сети Generator. Он выведет сгенерированные поддельные данные. Эти шаги также могут означать, что мы выбираем некоторые данные из распределения, которое изучил генератор. Мы обозначим шум как z_n, а сгенерированные данные как G(z_n). G(z_n) означает результат шума, обработанного генератором G.
  3. Мы объединяем сгенерированные поддельные данные с данными, взятыми из набора данных (которые являются реальными данными). Сделайте их входными в наш Дискриминатор. Мы обозначим это как D. Дискриминатор попытается узнать, предсказывая, являются ли данные поддельными или нет. Обучите нейронную сеть, выполнив прямой проход с последующим обратным распространением. Обновляет веса D.
  4. Затем нам нужно обучить Генератор. Нам нужно сделать G(z_n) или поддельные данные, сгенерированные из случайных шумов, в качестве входных данных для D. Обратите внимание, что эти шаги вводят только поддельные данные в Дискриминатор. Вперед передать G(z_n) в D. Используя нейронную сеть Дискриминатора, при выполнении прямого прохода предскажите, являются ли фальшивые данные фальшивыми или нет (D(G(z_n))). Затем выполните обратное распространение, при котором мы обновим только веса G.
  5. Повторяйте эти шаги до тех пор, пока мы не увидим, что генератор предоставляет хорошие поддельные данные или не достигнута максимальная итерация.

Иллюстрация следующая:

Обновив дистрибутив Генератора, чтобы он соответствовал Дискриминатору. Это то же самое, что минимизировать расхождение JS. Более подробную информацию вы можете прочитать в этой статье.

Чтобы наши агенты учились, убедитесь, что дискриминатор и генератор доминируют друг над другом. По возможности сбалансируйте их и заставьте дискриминатор и генератор учиться одновременно. Когда дискриминатор слишком силен (может различать поддельные и настоящие на 100%), Генератор также не может чему-либо научиться. Если в процессе тренировки мы дошли до этой точки, лучше ее прекратить. Обратное также действует, когда Генератор сильнее Дискриминатора. Это вызывает "Срушение режима", при котором наша модель всегда предсказывает один и тот же результат для любых случайных шумов. Это одна из самых сложных частей GAN, которая может кого-то расстроить.

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

Реализация

Итак, как устроена архитектура дискриминатора и генератора?

Это зависит от варианта GAN, который мы будем развивать. Поскольку мы будем использовать DCGAN, мы будем использовать несколько последовательных уровней CNN.

Мы будем использовать нестандартную архитектуру, отличную от оригинальной бумаги. Я следую архитектуре, использованной в книге François Chollet Deep Learning with Python с некоторыми изменениями .

Конфигурация, которую мы использовали для построения DCGAN, следующая:

latent_dim = 64
height = 64
width = 64
channels = 3

Это означает, что у нас будет 64 измерения скрытых переменных. Высота и ширина наших изображений - 64. Каждое изображение имеет 3 канала (R, G, B).

Вот импортированная библиотека и то, как подготавливаются данные:

Вот архитектура:

Генератор

Он состоит из слоев свертки, одним из которых является слой свертки и транспонирования. Чтобы увеличить размер изображения (32 -> 62), мы будем использовать параметр strides в слое свертки. Это сделано для того, чтобы избежать нестабильного обучения GAN.

Код

Дискриминатор

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

Код

GAN

Чтобы сделать возможным обратное распространение для генератора, мы создаем новую сеть в Keras, которая называется Генератором, за которым следует Дискриминатор. В этой сети мы замораживаем все веса, чтобы их вес не менялся.

Это сеть:

Обучение

Конфигурация обучения следующая:

iterations = 15000 
batch_size = 32

Конфигурация означает, что мы сделаем 15000 итераций. На каждой итерации мы обрабатываем 32 пакета реальных и поддельных данных (всего 64 для обучения дискриминатора).

Следуя грубым шагам, которые я объяснил выше, вот как мы шаг за шагом обучаем DCGAN:

  1. Выполните следующие шаги до максимального числа итераций
for step in tqdm_notebook(range(iterations)):

2. Создание случайного шума с распределением вероятностей, например нормальным распределением.

random_latent_vectors = np.random.normal(size = (batch_size, latent_dim))
generated_images = generator.predict(random_latent_vectors)

3. Объедините созданные поддельные данные с данными, взятыми из набора данных.

stop = start + batch_size
real_images = x_train[start: stop]
combined_images = np.concatenate([generated_images, real_images])
labels = np.concatenate([np.ones((batch_size,1)), 
                                    np.zeros((batch_size, 1))])

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

4. Добавьте шум к метке ввода

labels += 0.05 * np.random.random(labels.shape)

Это важный прием при обучении GAN.

5. Тренируйте дискриминатор

d_loss = discriminator.train_on_batch(combined_images, labels)

6. Обучите Генератор

random_latent_vectors = np.random.normal(size=(batch_size, 
                                                 latent_dim))
misleading_targets = np.zeros((batch_size, 1))
a_loss = gan.train_on_batch(random_latent_vectors, 
                              misleading_targets)

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

7. Обновите начальный индекс реального набора данных

start += batch_size
  
  if start > len(x_train) - batch_size:
    start = 0

Вот и все, вот полный код по обучению DCGAN:

Полученные результаты

Ладно, пусть начинается самое интересное! Мы начнем с визуализации сгенерированных изображений в разных средних точках. Прежде чем мы это сделаем, позвольте мне сказать вам, что это результат вышеупомянутой модели, которая обучена 20000 шагам (итерациям) и обучена с 30000 шагами. Модель обучается около 7 часов (~ 4300 шагов в час). Я назову модель с меньшим количеством шагов как Модель-A, а другую - как Модель-B.

Вот так!

Как читать

N ~ (x, y): скрытые векторы, случайно генерируемые в соответствии с нормальным распределением, которое имеет среднее значение x и стандартное отклонение y.

Результаты по скрытым векторам в N ~ (0,0.4) в Модели-A:

Неплохо, правда, есть изображения с асимметричным лицом.

Результаты по скрытым векторам на N ~ (0,1) в Модели-A:

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

Давайте изменим архитектуру на Модель-Б.

Результаты по скрытым векторам для N ~ (0,0.4) в Модели-B:

Ничего страшного, но лица темнеют. Интересно, что случилось с генератором.

Результаты по скрытым векторам на N ~ (0,1) на Модель-B:

Умм, ну ... на большинстве из них все еще есть лица мерзости. У некоторых из них были нормальные лица. Качество все еще почти такое же, как у Model-A. Хорошо ... Давайте изменим стандартное отклонение для следующих пакетов изображений как можно ближе к среднему. 0,4 было бы лучше всего.

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

Результаты по скрытым векторам для N ~ (-0,3,0,4) в Модели-A:

Результаты по скрытым векторам в N ~ (0,3,0,4) в Модели-A:

Результаты по скрытым векторам для N ~ (-0,3,0,4) на Модель-B:

Результаты по скрытым векторам на N ~ (0,3,0,4) на Модели-B:

Видите различия?

Да, посмотрите на их волосы. При среднем значении 0,3 волосы в основном черные (некоторые из них коричневые). Напротив, при среднем -0,3 волосы в основном желтые. Да, наша модель может помещать грани в соответствующие точки. Также Model-B генерирует лица темнее, чем A.

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

Давайте построим это:

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

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

Мы строим этот скрытый вектор, среднее значение которого равно:

[-1, -0.8, -0.6, -0.4, -0.2, 0, 0.2 0.4, 0.6, 0.8 ]

Первая строка - МОДЕЛЬ-A, вторая - МОДЕЛЬ-B. Манипулируя средним значением скрытых векторов, мы можем увидеть лицо, которое оно генерирует в этой точке. Мы это видим:

  • Чем ниже точка вектора, тем желтее волосы.
  • У него более темное лицо посередине. Это означает, что средние лица в наборе данных имеют этот стиль.
  • Чем положительнее точка вектора, тем голубее волосы. Позитивный латентный вектор также имеет более открытый рот при улыбке.

Основные операции линейной алгебры

Удивительно, не правда ли?

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

G + D

Грани GAN - результат добавления G к D. Вы можете увидеть, что волосы стали немного коричневыми. и волосы следуют стилю D справа и G слева.

Ниже приведены результаты других операций:

G - D (абсолютный)

Умножение на компоненты (G, D)

Управление скрытыми векторами

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

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

Например, мы хотим исследовать первые элементы в скрытом векторе, мы изменяем это измерение и оставляем другие без изменений.

Мы сгенерируем несколько лиц со средним значением в этих точках:

[-0.6, -0.3, 0.1, 0.3, 0.6]

Для каждой средней точки мы будем генерировать лица, размерность которых в скрытом векторе изменяется итеративно с этими значениями:

[-1.08031934, -0.69714143, -0.39691713, -0.12927146,  0.12927146, 0.39691713,  0.69714143,  1.08031934]

Позвольте визуализировать в выбранном нами измерении: (В этом разделе будет использоваться только МОДЕЛЬ-A)

28-е измерение:

Каковы цели 28-й скрытой переменной?

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

Посмотрим еще один!

5-е измерение

Каковы цели этой скрытой переменной?

Я думаю, что это как-то связано с левыми глазами, даже несмотря на то, что обработка левого глаза различается для каждой средней точки. Это также сделает волосы немного темнее. Что вы думаете?

11-е измерение

Думаю, это измерение заботит рот и правый глаз.

Другой пример на лицах, которые сгенерированы из средних точек только путем настройки скрытой переменной:

Вот и все. Мы можем нанести любые размеры на скрытые векторы и посмотреть, каковы их цели. Хотя иногда бывает трудно понять, для чего нужна скрытая переменная.

Сравнить с реальными данными

Давайте возьмем 8 реальных лиц из набора данных:

И образец 8 из generator N ~ (0,1) для моделей A и B:

Итак, если мы действуем как Дискриминатор, можем ли мы различать настоящие и поддельные лица?

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

Урок выучен

Ниже приведен урок, который я извлек после исследования DCGAN:

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

  • На самом деле, эта модель все еще не умеет генерировать фальшивые изображения. Тем не менее, он может создать несколько хороших лиц, хотя и не так хорош, как настоящий. Мы по-прежнему можем отличить поддельные изображения от реальных. Это связано с тем, что модель еще не охватила распределение реальных данных.
  • Модель ухудшает качество примерно на 26000 шагов. Вот где в моем эксперименте генератор стал слабым. Это нестабильность в GAN. Для этого мне нужно найти лучшую архитектуру. Мы видим, что результат на модели B становится темнее.
  • Итак, я разработал другую архитектуру с Batch Normalization и даже Dropout Layer. Угадай, что? При настройке архитектуры у меня есть два результата. Коллапс модели и доминирование дискриминатора. Думаю, разработать архитектуру GAN непросто.
  • Тем не менее, есть много советов и приемов по разработке хорошей GAN, которые я еще не реализовал. Возможно, нестабильность модели можно уменьшить, следуя этим советам.
  • Существует множество более стабильных вариантов GAN, таких как WGAN-DC, DRAGAN и SAGAN. Мне нужно использовать другую архитектуру, которая могла бы работать лучше, чем DCGAN.

Заключение

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

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

Даже в этом случае наша модель по-прежнему не может сделать лицо, которое заставило бы нас задуматься, поддельное это лицо или нет. Тем не менее, он все еще может формировать лица в стиле аниме.

Послесловие

Это мой первый опыт работы с GAN. Я стал лучше понимать, что делает GAN. Я также хочу исследовать свое любопытство по поводу того, что узнала GAN. Вот они, это действительно удивительно, что он на самом деле делает. Генератор может отображать случайный вектор, сгенерированный нормальным случайным шумом, в распределение данных. Он группирует лица в назначенные точки данных.

Выполняя GAN, я фактически запустил несколько моделей, которые я сделал вручную. Что ж, они с треском провалились. Однажды я подумал, что модель может быть успешной, но она перешла в режим коллапса (предсказанное лицо будет таким же, независимо от скрытого вектора). Я нашел репозиторий fchollet о DCGAN и слежу за его архитектурой.

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

Тем не менее, это действительно весело, и я хочу поэкспериментировать с другим вариантом GAN, таким как WGAN-GP, DRAGAN или SAGAN. Я лишь бегло просматриваю их содержание и хочу поэкспериментировать. Ожидайте статьи от проведения этих экспериментов 😃.

Этот мем на самом деле изображает этот эксперимент 😆.

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

Увидимся в моей следующей статье!

Репозиторий

См. Этот репозиторий GitHub:



В настоящее время я предоставляю IPython Notebook только для обучения GAN с нуля. Обратите внимание, если модель не выводит изображения в форме лица примерно на 200 итерациях, перезапустите обучение (запускается из раздела «Создать модель»).

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

Источники



Спасибо, Рену Ханделвал за отличную статью.

Https://www.cs.toronto.edu/~duvenaud/courses/csc2541/slides/gan-foundations.pdf

Https://github.com/fchollet/deep-learning-with-python-notebooks



Https://arxiv.org/pdf/1511.06434.pdf



Спасибо, Джонатан Хуэй за отличную статью.