Это 17-й день моего участия в августовском испытании обновлений. Узнайте подробности события:Испытание августовского обновления
Deep Learning with Python
Эта статья — одна из серии заметок, которые я написал, изучая Deep Learning with Python (второе издание, Франсуа Шолле). Содержимое статьи конвертировано из блокнотов Jupyter в Markdown, вы можете перейти наGitHubилиGiteeнайти оригинал.ipynb
ноутбук.
ты можешь идтиЧитайте оригинальный текст этой книги онлайн на этом сайте(Английский). Автор этой книги также дает соответствиеJupyter notebooks.
Эта статьяГлава 8 Генеративное глубокое обучение (Chapter 8. Generative deep learning) одной из записок.
Генеративно-состязательные сети
8.5 Introduction to generative adversarial networks
ГАН, китайскийГанГенеративно-состязательная сеть. Как и VAE, он используется для изучения скрытого пространства изображений. Эта штука может сделать сгенерированное изображение «статистически» неотличимым от реального изображения, то есть сделать сгенерированное изображение вполне реалистичным. Но в отличие от VAE, скрытое пространство GAN не обязательно имеет осмысленную структуру и является прерывистым.
ГАН состоит из двух частей:
- Сеть генераторов: введите случайный вектор (случайная точка в скрытом пространстве) и декодируйте его как изображение.
- Сеть дискриминатора: введите изображение (реальное или нарисованное генератором) и предскажите, является ли изображение реальным или созданным сетью генератора. (Сеть дискриминатора также называют «противником», противником)
Цель обучения GAN — дать возможность «сети генератора» обмануть «сеть дискриминатора».
Интуитивное понимание GAN — очень вдохновляющая история: то есть есть два человека, фальсификатор и оценщик. Фальсификаторы имитируют картины мастера, а затем смешивают свои имитации с оригиналами и передают их на оценку оценщику, который оценивает подлинность каждой картины и смотрит, какие из них подделаны. Из лучших побуждений оценщики дали обратную связь фальсификатору и рассказали ему, какими характеристиками обладает оригинал. Фальсификаторы шаг за шагом совершенствуют свою способность к подделке в соответствии с мнением оценщиков. Эти двое без устали повторяли процесс, так как фальсификатор все лучше и лучше копировал картину мастера, а оценщик все лучше находил подделку. В итоге фальсификаторы создали партию «аутентичных подделок», которые оценщики также безупречны.
Метод обучения GAN очень особенный, и его оптимизированное минимальное значение не является фиксированным. Наш обычный «градиентный спуск» — это скатывание с холма по местности со статическими потерями, но каждый шаг вниз по склону во время тренировки GAN меняет всю местность. Это динамическая система, процесс оптимизации которой представляет собой баланс между двумя силами. Таким образом, GAN трудно обучать. Для правильной работы GAN требуется много построения моделей и настройки гиперпараметров.
Глубокие сверточные генеративные состязательные сети
Попробуем реализовать простейшую GAN с помощью Keras. В частности, мы сделаемГлубокие сверточные генеративные состязательные сети(глубокая сверточная GAN, DCGAN), генератором и дискриминатором такого рода вещей являются глубокие сверточные нейронные сети.
Мы будем обучать DCGAN с изображениями категории «лягушка» в наборе данных CIFAR10. Этот набор данных содержит 50000 изображений RGB 32×32, принадлежащих 10 категориям (по 5000 изображений в каждой категории).
Процесс реализации
-
generator
Сеть будет иметь вид(latent_dim,)
Вектор отображает форму(32, 32, 3)
Изображение. -
discriminator
Сеть будет иметь вид(32, 32, 3)
Изображение сопоставляется с бинарной оценкой, которая оценивает вероятность того, что изображение соответствует действительности. -
gan
сеть будетgenerator
иdiscriminator
связанный:gan(x) = discriminator(generator(x))
. Эта сеть является результатом оценки сопоставления скрытого вектора с дискриминатором. - использовать с
"real"
или"fake"
Пометьте образцы реальных и поддельных изображений, чтобы обучить дискриминатор, используя обычные методы обучения обычных моделей классификации изображений. - Для обучения генератора используйте
gan
Градиент потерь модели относительно весов генератора. На каждом шаге перемещайте веса генератора в сторону «повышения вероятности того, что дискриминатор будет классифицировать изображение, декодированное генератором, как «истинное», то есть научить генератор обманывать дискриминатор.
Практические советы
Процесс обучения и настройки GAN очень сложен. Так что нам нужно вспомнить некоторые практические навыки, подытоженные предшественниками. Эти советы, как правило, полезны, но не во всех ситуациях. Под этими вещами нет теоретической базы, это все метафизика, поэтому я не объясняю и прямо не пишу вывод:
- Используйте tanh в качестве активации для последнего слоя генератора вместо сигмовидной.
- Точки в скрытом пространстве выбираются с использованием нормального распределения (распределения Гаусса) вместо равномерного распределения.
- Случайность повышает надежность. GAN могут "застрять" (достигнув неправильного гомеостаза) по-разному во время обучения. Введение случайности в процесс обучения помогает предотвратить это. Есть два способа ввести случайность:
- Использовать отсев в дискриминаторе;
- Добавьте случайный шум к меткам дискриминатора;
- Редкие градиенты могут помешать обучению GAN. «Операция максимального объединения» и «Активация ReLU» могут привести к разреженным градиентам, поэтому рекомендуется:
- Используйте «пошаговую свертку» вместо «максимального объединения» для понижения дискретизации;
- Используйте слои LeakyReLU вместо активаций ReLU;
- На сгенерированных изображениях часто видны артефакты шахматной доски из-за неравномерного пространственного охвата пикселей в генераторе. Решение этой проблемы состоит в том, чтобы сделать размер ядра кратным шагу всякий раз, когда пошаговое преобразование Conv2DTranpose или Conv2D используется как в генераторе, так и в дискриминаторе.
Пример графика артефактов шахматной доски из-за несоответствия размера шага и размера ядра:
реализация генератора
Начните строить первую ГАН для молодежи! !
Сначала разработайте модель генератора: преобразуйте вектор из скрытого пространства в изображение-кандидат.
Чтобы избежать «зависания» во время обучения, как в дискриминаторе, так и в генераторе используется отсев.
Генераторная сеть GAN:
from tensorflow import keras
from tensorflow.keras import layers
import numpy as np
height = 32
width = 32
channels = 3
latent_dim = 32
generator_input = keras.Input(shape=(latent_dim,))
# 将输入转换为大小为 16×16 的 128 个通道的特征图
x = layers.Dense(128 * 16 * 16)(generator_input)
x = layers.LeakyReLU()(x)
x = layers.Reshape((16, 16, 128))(x)
x = layers.Conv2D(256, 5, padding='same')(x)
x = layers.LeakyReLU()(x)
# 上采样为 32×32
x = layers.Conv2DTranspose(256, 4, strides=2, padding='same')(x)
x = layers.LeakyReLU()(x)
x = layers.Conv2D(256, 5, padding='same')(x)
x = layers.LeakyReLU()(x)
x = layers.Conv2D(256, 5, padding='same')(x)
x = layers.LeakyReLU()(x)
# 生成大小为 32×32 的特征图(CIFAR10 图像的形状)
x = layers.Conv2D(channels, 7, activation='tanh', padding='same')(x)
generator = keras.models.Model(generator_input, x)
generator.summary()
Реализация дискриминатора
Затем разработайте модель дискриминатора, возьмите изображение (реальное или синтетическое) и классифицируйте его как «истинное» (реальные изображения из обучающего набора) или «ложное» (изображения, нарисованные генератором).
Сеть дискриминатора GAN:
discriminator_input = layers.Input(shape=(height, width, channels))
x = layers.Conv2D(128, 3)(discriminator_input)
x = layers.LeakyReLU()(x)
x = layers.Conv2D(128, 4, strides=2)(x)
x = layers.LeakyReLU()(x)
x = layers.Conv2D(128, 4, strides=2)(x)
x = layers.LeakyReLU()(x)
x = layers.Conv2D(128, 4, strides=2)(x)
x = layers.LeakyReLU()(x)
x = layers.Flatten()(x)
x = layers.Dropout(0.4)(x)
x = layers.Dense(1, activation='sigmoid')(x)
discriminator = keras.models.Model(discriminator_input, x)
discriminator.summary()
discriminator_optimizer = keras.optimizers.RMSprop(
lr=0.0008,
clipvalue=1.0,
decay=1e-8)
discriminator.compile(optimizer=discriminator_optimizer,
loss='binary_crossentropy')
враждебная сеть
Наконец, GAN настроен для соединения генератора и дискриминатора вместе, чтобы преобразовать скрытые пространственные точки в истинное или ложное классификационное суждение.
При обучении этой модели «дискриминатор» нужно заморозить (чтобы его нельзя было обучить), и только «генератор» нужно двигать в сторону «улучшения способности обманывать дискриминатор».
При обучении гана все, что мы используем, это метки «реальных изображений», поэтому, если веса «дискриминатора» могут быть обновлены во время обучения, обучение заставит дискриминатор всегда предсказывать «истину».
Враждебная сеть:
discriminator.trainable = False # 这个只会作用于 gan
gan_input = keras.Input(shape=(latent_dim,))
gan_output = discriminator(generator(gan_input))
gan = keras.models.Model(gan_input, gan_output)
gan_optimizer = keras.optimizers.RMSprop(
lr=0.0004,
clipvalue=1.0,
decay=1e-8)
gan.compile(optimizer=gan_optimizer,
loss='binary_crossentropy')
Поезд DCGAN
Здесь мы используем набор данных CIFAR10 для обучения сети рисованию лягушек ?.
Поток тренировочного цикла выглядит следующим образом:
- Нарисуйте случайные точки (случайный шум) из скрытого пространства.
- Передайте этот случайный шум генератору для создания изображения.
- Смешайте сгенерированное изображение с реальным изображением.
- Дискриминатор обучается с использованием этих смешанных изображений вместе с соответствующими метками («истина» для реальных изображений и «ложь» для сгенерированных изображений).
- Случайным образом рисуйте новые точки в скрытом пространстве.
- Используйте эти случайные векторы и метки, которые являются «настоящими изображениями», для тренировки ган.
Конкретная реализация кода:
import os
import time
from tensorflow.keras.preprocessing import image
# 导入 CIFAR10 数据
(x_train, y_train), (_, _) = keras.datasets.cifar10.load_data()
# 选出青蛙?的图片
x_train = x_train[y_train.flatten() == 6]
x_train = x_train.reshape(
(x_train.shape[0],) + (height, width, channels)
).astype('float32') / 255.
iterations = 10000
batch_size = 20
save_dir = 'gan_save'
start = 0
for step in range(iterations+1):
start_time = time.time()
# 随机采样潜在点
random_latent_vectors = np.random.normal(size=(batch_size, latent_dim))
# 生成图像
generated_images = generator.predict(random_latent_vectors)
# 选取真实图像
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))])
labels += 0.05 * np.random.random(labels.shape) # 向标签中添加随机噪声
# 训练判别器
d_loss = discriminator.train_on_batch(combined_images, labels)
# 随机采样潜在点
random_latent_vectors = np.random.normal(size=(batch_size, latent_dim))
misleading_targets = np.zeros((batch_size, 1)) # 谎称全部都是真实图片
# 通过 gan 模型训练生成器
a_loss = gan.train_on_batch(random_latent_vectors, misleading_targets)
end_time = time.time()
start += batch_size
if start > len(x_train) - batch_size:
start = 0
if step % 50 == 0:
gan.save_weights('gan.h5')
print(f'step {step}: discriminator loss: {d_loss}, adversarial loss: {a_loss}')
img = image.array_to_img(generated_images[0] * 255., scale=False)
img.save(os.path.join(save_dir, f'generated_frog_{step}.png'))
img = image.array_to_img(real_images[0] * 255., scale=False)
img.save(os.path.join(save_dir, f'real_frog_{step}.png'))
else:
time_cost = end_time - start_time
time_eta = time_cost * (iterations - step)
print(f'{step}/{iterations}: {time_cost:.2f}s - ETA: {time_eta:.0f}s', end='\r')
Запустите код после длительного процесса обучения:
step 0: discriminator loss: 0.6944685578346252, adversarial loss: 0.7566524744033813
...
step 500: discriminator loss: 0.7020201683044434, adversarial loss: 0.7446410059928894
...
step 1000: discriminator loss: 0.7096449136734009, adversarial loss: 0.752526581287384
Окончательное выходное изображение:
Эффект совсем плохой. Эта штука отнимает слишком много времени на обучение, и ЦП напрямую уговаривают сдаться.Мой маленький MacBook пробежал всего 1000 раундов, что слишком мало. . . Так что я не вижу, что, черт возьми, здесь происходит. ?
Немного больше тренировок на графическом процессоре должно дать гораздо лучшие результаты, чем это. Просто попробуйте сами.
See you.