Подробное объяснение и реализация BicycleGAN (реализовано с помощью TensorFlow2)

искусственный интеллект глубокое обучение
Подробное объяснение и реализация BicycleGAN (реализовано с помощью TensorFlow2)

«Это 16-й день моего участия в ноябрьском испытании обновлений. Подробную информацию об этом событии см.:Вызов последнего обновления 2021 г."

Разнообразная трансформация изображения с BicyleGAN

Pix2pixиCycleGAN— это очень популярная GAN, имеющая множество вариантов не только в академических кругах, но и множество приложений, основанных на ней. Однако у них обоих есть недостаток — вывод изображения почти всегда будет выглядеть одинаково. Например, если бы мы выполнили преобразование зебры в лошадь, преобразованная фотография одной и той же лошади всегда имела бы одинаковый внешний вид и оттенок. Это связано с неотъемлемыми свойствами GAN, которые учатся отфильтровывать случайный шум. Чтобы выполнить разнообразное преобразование изображений, в этой статье подробно объясняетсяBicycleGANКак решить эту проблему, чтобы создавать более насыщенные изображения и использовать преимуществаTensorflow2ДостигнутоBicycleGAN.

Отображение эффекта BicyleGAN

использоватьBicyleGANИзображение может быть преобразовано различными способами для получения различных стилей и цветов:

BicyleGAN效果展示 BicyleGAN效果展示 BicyleGAN效果展示

Архитектура BicycleGAN

с началаBicycleGANПеред этим позвольте мне дать вам краткое введение. Когда вы слышите это имя в первый раз, вы можете подуматьBicycleGANдаCycleGANвариант , но на самом деле это связано сCycleGANЭто не имеет значения, вместо этого это правильноpix2pixулучшение.

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

На картинке нижеBicycleGANСвязанные модели и конфигурации.图(a)это конфигурация для вывода, изображение A комбинируется с шумом для создания изображенияB^\hat B, что можно рассматривать какcGAN. В BicyleGAN форма(256, 256, 3)Образ А обусловлен, в то время как скрытое кодированиеzzДискретизированный шум представляет собой одномерный вектор размера 8.图(b)даpix2pix + 噪声конфигурация обучения. и图(c)и图(d)Две конфигурации задаютсяBicycleGANИспользуйте при обучении:

BicycleGAN架构

короче,BicycleGANМожно найти взаимосвязь между скрытым кодированием z и целевым изображением B, поэтому генератор может научиться генерировать разные изображения при разных z.B^\hat B. Как показано на рисунке выше, BicycleGAN работает, комбинируяcVAE-GANиcLR-GANОбе модели делают это.

cVAE-GAN

VAE-GANАвторы утверждают, что,L1L_1Потери не являются хорошей мерой визуального качества изображения. Например, если изображение сдвинуто на несколько пикселей вправо, человеческий глаз может выглядеть так же, но это приведет к увеличению изображения.L1L_1потеря. Так что используйтеGANДискриминатор, чтобы узнать целевую функцию, чтобы судить, является ли поддельное изображение реальным, и использоватьVAEКак генератор, результирующие изображения более четкие. Если игнорировать图(c)изображения вAA,ЭтоVAE-GAN, так какAAэто условие, оно становится условиемcVAE-GAN. Этапы обучения следующие:

  1. VAEпоставить реальные фотографииBBКодируйте скрытые коды в многомерные распределения Гаусса, а затем производите выборку из них для создания зашумленных входных данных. Этот процесс является стандартным рабочим процессом VAE;
  2. использовать изображенияAAкак условие и из скрытого вектораzzДискретизированный шум используется для создания поддельных изображений.B^\hat B.

Поток данных в обученииB>z>B^B->z->\hat B ( 图(c)сплошные стрелки на ), функция полных потерь состоит из трех потерь:

  1. LGANVAE\mathcal L_{GAN}^{VAE}: против потери
  2. L1VAE\mathcal L_1^{VAE}:L1L_1потеря реконструкции
  3. LKL\mathcal L_{KL}:KLKLПотеря дивергенции

cLR-GAN(Conditional Latent Regressor GAN)

существуетcVAE-GAN, реальное изображение B кодируется, чтобы обеспечить реальную выборку скрытого вектора и выборку из него. но,cLR-GANобрабатывается по-другому, который сначала использует генератор для создания поддельных изображений из случайного шумаB^\hat B, а затем для поддельного изображенияB^\hat BЗакодируйте и, наконец, вычислите его отличие от входного случайного шума. Шаги прямого расчета следующие:

  1. Во-первых, что-то вродеcGAN, случайным образом сгенерируйте некоторый шум, затем объедините изображение A, чтобы создать поддельное изображение.B^\hat B.
  2. После этого используйтеVAE-GANТот же кодировщик псевдоизображенияB^\hat Bкодируются как скрытые векторы.
  3. Наконец, образец из закодированного латентного вектораz^\hat z, а с входным шумомzzПодсчитайте потери.

   Поток данныхz>B^>z^z-> \hat B -> \hat z ( 图(d)сплошные стрелки в ), есть две потери:

  1. LGAN\mathcal L_{GAN}: против потери
  2. L1latent\mathcal L_1^{latent}:шумN(z)между базовым кодомL1L_1потеря

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

lossBicycle=LGANVAE+LGAN+λL1VAE+λlatentL1latent+λKLloss_{велосипед}=\mathcal L_{GAN}^{VAE}+\mathcal L_{GAN}+λ\mathcal L_1^{VAE}+λ_{скрытый}\mathcal L_1^{скрытый}+λ_{KL}

В конфигурации по умолчаниюλ=10λ = 10,λlatent=0.5λ_{скрытый} = 0,5,λlatent=0.01λ_{скрытый} = 0,01.

Реализация BicycleGAN

BicycleGANСуществует три типа сетей - генератор, дискриминатор и кодер. заcVAE-GANиcLR-GANИспользование отдельных дискриминаторов улучшает качество изображения, поэтому мы будем использовать четыре сети — генератор, кодировщик и два дискриминатора.

Вставьте скрытую кодировку в генератор

Есть два способа вставить скрытые кодировки в генератор, как показано на следующем рисунке:

  1. Объединить с входным изображением;
  2. Вставьте его в другие слои в пути понижающей дискретизации генератора.

在生成器中插入潜在编码

Эксперименты показали, что первый работает хорошо. Существуют различные способы комбинирования входов и условий разной формы.BicycleGANИспользуемый метод заключается в повторном скрытом кодировании несколько раз, а затем объединении с входным изображением.

существуетBicycleGAN, длина скрытого кода равна 8, мы извлекли 8 выборок из распределения шума, и каждая выборка была повторена H × W раз, чтобы сформировать форму(H, W, 8)тензор. Другими словами, в 8 каналах его(H,W)Карты объектов все одинаковые. Следующий код показывает объединение и объединение возможных кодировок:

input_image = layers.Input(shape=image_shape, name='input_image')
input_z = layers.Input(shape=(self.z_dim,), name='z')
z = layers.Reshape((1,1, self.z_dim))(input_z)
z_tiles = tf.tile(z, [self.batch_size, self.input_shape[0], self.input_shape[1], self.z_dim])
x = layers.Concatenate()([input_image, z_tiles])

Следующим шагом является создание двух моделей, а именноcVAE-GANиcLR-GAN, чтобы объединить сети и создать прямой поток информации.

cVAE-GAN

Создать нижеcVAE-GANКод модели, реализация форвардного расчета:

images_A_1 = layers.Input(shape=input_shape, name='ImageA_1')
images_B_1 = layers.Input(shape=input_shape, name='ImageB_1')
z_encode, self.mean_encode, self.logvar_encode = self.encoder(images_B_1)
fake_B_encode = self.generator([images_A_1, z_encode])
encode_fake = self.discriminator_1(fake_B_encode)
encode_real = self.discriminator_1(images_B_1)
kl_loss =  - 0.5 * tf.reduce_sum(1 + self.logvar_encode - tf.square(self.mean_encode) - tf.exp(self.logvar_encode))
self.cvae_gan = Model(inputs=[images_A_1, images_B_1], outputs=[encode_real, encode_fake, fake_B_encode, kl_loss])

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

cLR-GAN

НижеcLR-GANРеализация форвардного расчета:

images_A_2 = layers.Input(shape=input_shape, name='ImageA_2')
images_B_2 = layers.Input(shape=input_shape, name='ImageB_2')
z_random = layers.Input(shape=(self.z_dim,), name='z')
fake_B_random = self.generator([images_A_2, z_random])
_, mean_random, _ = self.encoder(fake_B_random)
random_fake = self.discriminator_2(fake_B_random)
random_real = self.discriminator_2(images_B_2)      
self.clr_gan = Model(inputs=[images_A_2, images_B_2,   z_random],
			outputs=[random_real, random_fake, mean_random])

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

этапы обучения

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

images_A_1, images_B_1 = next(data_generator)
images_A_2, images_B_2 = next(data_generator)
self.train_step(images_A_1, images_B_1, images_A_2, images_B_2)

Мы можем проводить обучение, используя два разных метода. Один из них — определить и скомпилировать модель Keras с оптимизатором и функцией потерь, а затем вызватьtrain_on_batch()для выполнения шага обучения, который хорошо работает на четко определенных моделях. Кроме того, мы также можем использоватьtf.GradientTapeчтобы лучше контролировать обновления градиента.BicycleGANЕсть две модели, у них общий генератор и энкодер, но нам нужно обновить их параметры с разными комбинациями функций потерь, что делаетtrain_on_batchметод невозможен без изменения исходных настроек. Поэтому мы используемtf.GradientTapeОбъедините генератор и дискриминатор этих двух моделей в один шаг обучения следующим образом:

  1. Первый шаг — выполнить прямой проход и собрать выходные данные обеих моделей:
    def train_step(self, images_A_1, images_B_1, images_A_2, images_B_2):
        z = tf.random.normal((self.batch_size, self.z_dim))
        real_labels = tf.ones((self.batch_size, self.patch_size, self.patch_size, 1))
        fake_labels = tf.zeros((self.batch_size, self.patch_size, self.patch_size, 1))

        with tf.GradientTape() as tape_e, tf.GradientTape() as tape_g, tf.GradientTape() as tape_d1, tf.GradientTape() as tape_d2:
            encode_real, encode_fake, fake_B_encode, kl_loss = self.cvae_gan([images_A_1, images_B_1])
            random_real, random_fake, mean_random = self.clr_gan([images_A_2, images_B_2, z])
  1. Затем мы обновляем дискриминатор с помощью обратного распространения градиента:
            # discriminator loss
            self.d1_loss = self.mse(real_labels, encode_real) + self.mse(fake_labels, encode_fake)
            gradients_d1 = tape_d1.gradient(self.d1_loss, self.discriminator_1.trainable_variables)
            self.optimizer_d1.apply_gradients(zip(gradients_d1, self.discriminator_1.trainable_variables))

            self.d2_loss = self.mse(real_labels, random_real) + self.mse(fake_labels, random_fake)
            gradients_d2 = tape_d2.gradient(self.d2_loss,self.discriminator_2.trainable_variables)
            self.optimizer_d2.apply_gradients(zip(gradients_d2, self.discriminator_2.trainable_variables))
  1. Затем мы рассчитываем потери на основе выходных данных модели. иCycleGANсходство,BicycleGANтакже использоватьLSGANФункция потерь, среднеквадратическая ошибка:
			self.LAMBDA_IMAGE = 10
			self.LAMBDA_LATENT = 0.5
			self.LAMBDA_KL = 0.01
            # Generator and Encoder loss
            self.gan_1_loss = self.mse(real_labels, encode_fake)
            self.gan_2_loss = self.mse(real_labels, random_fake)
            self.image_loss = self.LAMBDA_IMAGE * self.mae(images_B_1, fake_B_encode)
            self.kl_loss = self.LAMBDA_KL * kl_loss
            self.latent_loss = self.LAMBDA_LATENT * self.mae(z, mean_random)
  1. Наконец, есть обновления весов генератора и энкодера.L1L_1Скрытая потеря кодирования используется только для обновления генератора, а не кодировщика. Одновременная оптимизация потерь заставит их скрывать информацию, относящуюся к скрытому кодированию, без изучения значимых закономерностей в скрытом кодировании. Следовательно, потери необходимо вычислять отдельно для генератора и энкодера и соответственно обновлять веса:
            encoder_loss = self.gan_1_loss + self.gan_2_loss + self.image_loss + self.kl_loss
            generator_loss = encoder_loss + self.latent_loss

            gradients_generator = tape_g.gradient(generator_loss, self.generator.trainable_variables)
            self.optimizer_generator.apply_gradients(zip(gradients_generator, self.generator.trainable_variables))
            
            gradients_encoder = tape_e.gradient(encoder_loss, self.encoder.trainable_variables)
            self.optimizer_encoder.apply_gradients(zip(gradients_encoder, self.encoder.trainable_variables))

Результаты обучения и анализ

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

BicyleGAN效果展示 BicyleGAN效果展示 BicyleGAN效果展示

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