Подробное руководство по использованию вариационного автоэнкодера для создания новых лиц.

Вступление

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

В этом руководстве рассматриваются основы генеративного глубокого обучения с помощью вариационных автоэнкодеров. Я предполагаю, что вы достаточно знакомы с концепциями сверточных нейронных сетей и обучения представлениям. Если нет, я бы порекомендовал посмотреть Лекционные видео CS231n Андрея Карпати, поскольку они, по моему честному мнению, лучший ресурс для изучения CNN в Интернете. Вы также можете найти конспекты лекций по курсу здесь.

В этом примере демонстрируется процесс создания и обучения VAE с использованием Keras для создания новых лиц. Мы будем использовать набор данных CelebFaces Attributes (CelebA) Dataset от Kaggle и Google Colab для обучения модели VAE.

Генеративные модели

Если вы только начинаете изучать область генеративного глубокого обучения, вариационный автоэнкодер (VAE) идеально подойдет для начала. Архитектура VAE интуитивно понятна и проста для понимания. В отличие от дискриминационной модели, такой как классификатор CNN, генеративная модель пытается изучить базовое распределение данных, а не классифицировать данные по одной из многих категорий. Хорошо обученный классификатор CNN будет очень точно отличать изображение автомобиля от изображения дома. Однако это не решает нашей задачи по созданию изображений автомобилей и домов.

Дискриминантная модель учится собирать полезную информацию из данных и использовать эту информацию для классификации новой точки данных по одной из двух или более категорий. С вероятностной точки зрения дискриминативная модель оценивает вероятность 𝑝 (𝑦 | 𝑥), где 𝑦 - категория или класс, а 𝑥 - точка данных. Он оценивает вероятность принадлежности точки к категории 𝑦. Например, вероятность того, что на изображении изображен автомобиль или дом.

Генеративная модель изучает базовое распределение данных, которое объясняет, как данные были сгенерированы. По сути, он имитирует базовое распределение и позволяет нам брать из него образцы для генерации новых данных. Его можно определить как оценку вероятности 𝑝 (𝑥), где 𝑥 - точка данных. Он оценивает вероятность наблюдения точки данных 𝑥 в распределении.

Простой автоэнкодер

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

Простой или ванильный автоэнкодер состоит из двух нейронных сетей - кодировщика и декодера. Кодировщик отвечает за преобразование изображения в компактный вектор более низкой размерности (или скрытый вектор). Этот скрытый вектор представляет собой сжатое представление изображения. Таким образом, кодировщик отображает входные данные из входного пространства более высоких измерений в скрытое пространство более низких измерений. Это похоже на классификатор CNN. В классификаторе CNN этот скрытый вектор будет впоследствии передан на уровень softmax для вычисления вероятностей отдельных классов. Однако в автоэнкодере этот скрытый вектор подается в декодер. Декодер - это другая нейронная сеть, которая пытается восстановить изображение, тем самым отображая скрытое пространство более низкой размерности в выходное пространство более высокой размерности. Кодировщик и декодер выполняют сопоставления, которые точно противоположны друг другу, как показано на изображении img-1.

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

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

Уборка

Скачивание набора данных

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

Загрузите Kaggle.json, загруженный из вашей зарегистрированной учетной записи kaggle.

!pip install -U -q kaggle
!mkdir -p ~/.kaggle

Загрузите набор данных из Kaggle.

!cp kaggle.json ~/.kaggle/
!kaggle datasets download -d jessicali9530/celeba-dataset

Определение структуры проекта.

ВНИМАНИЕ (для пользователей Colab): не пытайтесь исследовать каталог с помощью средства просмотра слева, поскольку страница перестает отвечать из-за большого размера набора данных.

Импорт

Данные

Поскольку набор данных довольно велик, мы создадим объект ImageDataGenerator и будем использовать его функцию-член - flow_from_directory, чтобы определять поток данных непосредственно с диска, а не загружать весь набор данных в объем памяти. ImageDataGenerator также можно использовать для динамического применения различных преобразований для увеличения изображения, что особенно полезно в случае небольших наборов данных.

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

Модель Архитектура

Сборка кодировщика

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

ПРИМЕЧАНИЕ. Комбинация padding = ‘same’ и stride = 2 даст выходной тензор, равный половине размера входного тензора как по высоте, так и по ширине. На глубину / каналы это не повлияет, поскольку они численно равны количеству фильтров.

Сборка декодера

Напомним, что функция декодера - восстановить изображение по скрытому вектору. Следовательно, необходимо определить декодер, чтобы постепенно увеличивать размер активаций по сети. Это может быть достигнуто либо через слой UpSampling2D, либо через слой Conv2DTransponse.

Здесь используется слой Conv2DTranspose. Этот слой создает выходной тензор, вдвое превышающий размер входного тензора как по высоте, так и по ширине.

ПРИМЕЧАНИЕ. В этом примере декодер определяется как зеркальное отображение кодировщика, что не является обязательным.

Присоединение декодера к кодировщику

Составление и обучение

Используемая функция потерь представляет собой простую среднеквадратическую ошибку (RMSE). Истинный выход - это тот же самый пакет изображений, который был передан модели на ее входном уровне. Оптимизатор Adam оптимизирует ошибку RMSE для кодирования пакета изображений в соответствующие скрытые векторы и последующего их декодирования для восстановления изображений.

Обратный вызов ModelCheckpoint Keras сохраняет веса модели для повторного использования. Он перезаписывает файл новым набором весов после каждой эпохи.

ПРИМЕЧАНИЕ. Если вы используете Google Colab, загрузите веса на диск или смонтируйте свой Google Диск.

СОВЕТ. Вот действительно полезный совет, который я нашел на Reddit (автор arvind1096): чтобы предотвратить отключение Google Colab из-за тайм-аута, выполните следующую функцию JS в консоли Google Chrome.

function ClickConnect(){console.log(“Working”);document.querySelector(“colab-toolbar-button#connect”).click()}setInterval(ClickConnect,60000)

Реконструкция

Первым шагом является создание нового пакета изображений с помощью ImageDataGenerator, определенного в разделе «Данные» вверху. Изображения возвращаются в виде массива, и количество изображений равно BATCH_SIZE.

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

ПРИМЕЧАНИЕ. Причина, по которой изображениям не хватает резкости, связана с потерей RMSE, поскольку они усредняют различия между значениями отдельных пикселей.

Недостатки

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

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

Попытка сгенерировать изображения из латентных векторов, выбранных из стандартного нормального распределения

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

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

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

Вариационный автоэнкодер

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

Как можно модифицировать простой автоэнкодер, чтобы преобразовать кодировщик в z-мерное стандартное нормальное распределение?

Любое z-мерное нормальное распределение может быть представлено вектором среднего и ковариационной матрицей Σ.

Следовательно, в случае VAE, кодировщик должен вывести средний вектор ( 𝜇 ) и ковариационную матрицу ( Σ ) для сопоставления с нормальным распределением, правильно?

Да, однако, в ковариационную матрицу Σ следует внести небольшие изменения.

Модификация 1) Предполагается, что между элементами скрытого вектора нет корреляции. Следовательно, все недиагональные элементы, представляющие ковариацию, равны нулю. Таким образом, ковариационная матрица является диагональной матрицей.

Поскольку диагональные элементы этой матрицы представляют дисперсию, она просто представлена ​​как 𝜎2, z-мерный вектор дисперсии.

Модификация 2). Напомним, что дисперсия может принимать только неотрицательные значения. Чтобы гарантировать, что выходной сигнал кодировщика не ограничен, кодировщик фактически сопоставляется со средним вектором и логарифмом вектора дисперсии. Логарифм гарантирует, что выходной сигнал теперь может принимать любое значение в диапазоне (−∞, ∞). Это упрощает обучение, поскольку выходы нейронной сети, естественно, неограниченны.

Теперь, как обеспечить соответствие кодировщика стандартному нормальному распределению (т. е. со средним значением 0 и стандартным отклонением 1)?

Введите дивергенцию KL.

Дивергенция KL позволяет измерить степень, в которой одно распределение вероятностей отличается от другого. Расхождение KL между распределением со средним и стандартным отклонением 𝜎 и стандартным нормальным распределением принимает форму:

Путем небольшого изменения функции потерь, чтобы включить потерю дивергенции KL в дополнение к потерям RMSE, VAE вынужден гарантировать, что кодирование очень похоже на многомерное стандартное нормальное распределение. Поскольку многомерное стандартное нормальное распределение имеет нулевое среднее значение, оно сосредоточено вокруг начала координат. Сопоставление каждого изображения со стандартным нормальным распределением, а не с фиксированной точкой, гарантирует, что скрытое пространство является непрерывным, а скрытые векторы центрируются вокруг начала координат.

Взгляните на изображение img-2 для лучшего понимания.

Если кодировщик отображает 𝜇 и 𝜎 вместо z-мерного латентного вектора, какой ввод будет передан декодеру во время обучения?

Входные данные декодера, как показано в img-2, представляют собой вектор, выбранный из нормального распределения, представленного выходными данными кодировщика - 𝜇 и 𝜎. Эта выборка может быть сделана следующим образом:

𝑍=𝜇+𝜎𝜀

где 𝜀 выбирается из многомерного стандартного нормального распределения.

Как мы создаем новые лица?

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

Декодер требует доработки?

Нет, декодер остался прежним. Он идентичен простому автоэнкодеру.

Архитектура модели VAE

В архитектуру кодировщика внесены незначительные изменения. Теперь у него есть два выхода: mu (среднее) и log_var (логарифм дисперсии), как показано ниже.

Сборка декодера

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

Присоединение декодера к кодеру

Составление и обучение

Функция потерь представляет собой сумму RMSE и расхождения KL. Потерям RMSE присваивается вес, известный как коэффициент потерь. Коэффициент потерь умножается на потери RMSE. Если мы используем высокий коэффициент потерь, начинают проявляться недостатки простого автоэнкодера. Однако, если мы используем слишком низкий коэффициент потерь, качество восстановленных изображений будет плохим. Следовательно, коэффициент потерь - это гиперпараметр, который необходимо настроить.

Реконструкция

Процесс восстановления такой же, как и в Простом автоэнкодере.

Результаты реконструкции очень похожи на результаты простого автоэнкодера.

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

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

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

Замечено, что первые 30 элементов размерного вектора Z очень похожи на стандартное нормальное распределение. Таким образом, добавление члена KL-дивергенции оправдано.

Заключение

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

Записную книжку для этого руководства можно получить из моего репозитория GitHub.

Для дальнейшего чтения я бы порекомендовал Учебник по вариационным автоэнкодерам Карла Дёрша .

Не стесняйтесь связываться со мной в LinkedIn и GitHub.

Спасибо за прочтение!