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

Существует несколько различных типов GAN, каждый со своими уникальными характеристиками и вариантами использования. Некоторые из самых популярных типов включают в себя:

  1. Vanilla GAN: это оригинальная архитектура GAN, состоящая из генератора и сети дискриминатора.
  2. DCGAN (глубокая сверточная GAN). Этот тип GAN использует сверточные слои как в сети генератора, так и в сети дискриминатора, что позволяет генерировать изображения с высоким разрешением.
  3. WGAN (Wasserstein GAN): этот тип GAN заменяет традиционную функцию потерь GAN на расстояние Вассерштейна, которое более стабильно во время обучения и приводит к меньшему коллапсу моды.
  4. LSGAN (GAN методом наименьших квадратов): этот тип GAN использует функцию потерь методом наименьших квадратов вместо традиционной бинарной кросс-энтропийной потери, что обеспечивает более стабильный процесс обучения.
  5. CycleGAN: этот тип GAN может переводить изображение из одного домена в другой, например, преобразовывая изображение лошади в изображение зебры.
  6. BEGAN (Генеративные состязательные сети с граничным равновесием): Этот тип GAN использует цель равновесия, которая помогает стабилизировать процесс обучения, а также обеспечивает меру качества сгенерированных изображений.
  7. BigGAN. Этот тип GAN представляет собой большую генеративно-состязательную сеть, которая генерирует изображения высокого разрешения с мелкими деталями и высоким качеством.

Это одни из самых популярных типов GAN, но регулярно предлагаются новые варианты и архитектуры.

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





Введение в набор данных

Набор данных рукописных цифр — это набор изображений рукописных цифр вместе с соответствующими им метками (т. е. правильной цифрой). Эти наборы данных обычно используются для обучения и оценки алгоритмов машинного обучения, особенно в области распознавания изображений и компьютерного зрения. Одним из самых популярных наборов данных этого типа является набор данных MNIST, который состоит из 60 000 обучающих изображений и 10 000 тестовых изображений рукописных цифр вместе с соответствующими им метками. Мы собираемся использовать этот набор данных для обучения нашей модели GAN. Изображения в этом наборе данных имеют размер 28x28 пикселей и изображения в градациях серого, и он широко используется в качестве эталонного набора данных для тестирования и сравнения производительности различных алгоритмов машинного обучения. Другие примеры наборов данных рукописных цифр включают набор данных USPS, который содержит изображения почтовых индексов USPS, и набор данных KMNIST, который представляет собой более разнообразный набор рукописных цифр, чем набор данных MNIST.

from matplotlib import pyplot 
# load the images into memory
(trainX, trainy), (testX, testy) = load_data()
# plot images from the training dataset
fig, ax = pyplot.subplots(ncols = 5,nrows=5,figsize=(20,20))
for i in range(5):
  for j in range(5):
      ax[i][j].imshow(trainX[(i+1)*(j+1)-1], cmap='gray_r')

Как видно из приведенного выше рисунка, набор данных состоит из изображений рукописных цифр, пронумерованных от 0 до 9.

Предварительная обработка

  1. Масштабирование функций. Масштабирование функций – важный этап предварительной обработки данных. Если масштабирование функций не выполняется, алгоритм машинного обучения будет считать большие значения более высокими, а меньшие – меньше, независимо от единицы измерения. Используемый здесь масштабатор — это традиционный масштабатор MinMax. Масштабатор Minmax Преобразование объектов путем масштабирования каждого объекта до заданного диапазона [0,1]
  2. Развернуть размеры. В библиотеке Python NumPy numpy.expand_dims — это функция, которая вставляет новую ось с размером 1. Она принимает входной массив и положение оси, в которое должна быть вставлена ​​новая ось. Он возвращает новый массив со вставленным новым измерением.
def load_real_samples():
  # load mnist dataset
  (trainX, _), (_, _) = load_data()
  # expand to 3d, e.g. add channels dimension
  X = np.expand_dims(trainX, axis=-1)
  # convert from unsigned ints to floats
  X = X.astype('float32')
  # scale from [0,255] to [0,1]
  X = X / 255.0
  return X

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

Работа GAN

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

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

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

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

Маркировка настоящих и поддельных образцов

def generate_real_samples(dataset, n_samples):
  # choose random instances
  ix = randint(0, dataset.shape[0], n_samples)
  # retrieve selected images
  X = dataset[ix]
  # generate ✬real✬ class labels (1)
  y = ones((n_samples, 1))
  return X, y

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

# generate n fake samples with class labels
def generate_fake_samples(n_samples):
  # generate uniform random numbers in [0,1]
  X = rand(28 * 28 * n_samples)
  # reshape into a batch of grayscale images
  X = X.reshape((n_samples, 28, 28, 1))
  # generate ✬fake✬ class labels (0)
  y = zeros((n_samples, 1))
  return X, y

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

Давайте сначала построим нашу модель дискриминатора, потому что их очень легко построить по сравнению с моделью генератора.

Построение модели дискриминатора

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

  1. Работа модели дискриминатора состоит в том, чтобы просто классифицировать входные образцы как настоящие или поддельные, тогда как работа генератора состоит в том, чтобы создавать реалистично выглядящие образцы, которые могут обмануть дискриминатор. Эта задача бинарной классификации обычно считается более простой, чем задача создания новых образцов.
  2. Модели дискриминатора часто можно обучать с использованием стандартных методов обучения с учителем, таких как обратное распространение ошибки и стохастический градиентный спуск, тогда как модели генератора требуют более продвинутых методов, таких как обучение без учителя или обучение с подкреплением.
  3. Модели дискриминатора имеют меньше параметров для изучения и, следовательно, требуют меньше данных для обучения.
  4. Модели дискриминатора обучены распознавать реальные данные и поддельные данные. Реальные данные уже доступны, что упрощает процесс различения реальных и поддельных данных.
  5. В генераторе и дискриминаторе архитектуры GAN модели обучаются одновременно. По мере того, как генератор становится лучше в генерации поддельных данных, дискриминатор становится лучше в обнаружении поддельных данных.

Вот некоторые из причин, которые делают обучение модели дискриминатора относительно простым.

def define_discriminator_model(in_shape=(28,28,1)):
  model = Sequential()
  model.add(Conv2D(64, (3,3), strides=(2,2), padding='same', input_shape = in_shape))
  model.add(LeakyReLU(0.2))
  model.add(Dropout(0.2))
  model.add(Conv2D(64, (3,3), strides=(2,2), padding ='same'))
  model.add(LeakyReLU(0.2))
  model.add(Dropout(0.2))
  model.add(Flatten())
  model.add(Dense(1, activation='sigmoid'))

  opt = Adam(lr=0.0002, beta_1=0.5)
  model.compile(loss='binary_crossentropy', optimizer=opt, metrics=['accuracy'])
  return model
# 2x2 strides causes the downsampling from 14,14 to 7,7 
model = define_discriminator_model()
model.summary()

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

Тренировка дискриминатора

def train_discriminator(model, dataset, n_iter=100, n_batch=256):
  half_batch = int(n_batch / 2)
  # manually enumerate epochs
  for i in range(n_iter):
  # get randomly selected ✬real✬ samples
    X_real, y_real = generate_real_samples(dataset, half_batch)
    # update discriminator on real samples
    _, real_acc = model.train_on_batch(X_real, y_real)
    # generate ✬fake✬ examples
    X_fake, y_fake = generate_fake_samples(half_batch)
    # update discriminator on fake samples
    _, fake_acc = model.train_on_batch(X_fake, y_fake)
    # summarize performance
    print('>%d real=%.0f%% fake=%.0f%%' % (i+1, real_acc*100, fake_acc*100))
# define the discriminator model
model = define_discriminator_model()
# load image data
dataset = load_real_samples()
# fit the model
train_discriminator(model, dataset)

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

Поскольку дискриминатор готов, давайте рассмотрим построение модели генератора.

Модель генератора зданий

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

  1. Работа модели генератора заключается в создании реалистично выглядящих выборок, которые могут обмануть дискриминатор, что является более сложной задачей, чем простая классификация входных выборок как настоящих или поддельных.
  2. Модели генератора требуют более продвинутых методов, таких как обучение без учителя или обучение с подкреплением, тогда как модели дискриминатора часто можно обучать с использованием стандартных методов обучения с учителем.
  3. Модели генераторов имеют больше параметров для изучения и, следовательно, требуют больше данных для обучения.
  4. Модель генератора обучена генерировать поддельные данные, которые могут обмануть дискриминатор. По мере того, как дискриминатор становится лучше в обнаружении поддельных данных, генератору становится все труднее генерировать такие данные, которые могут обмануть дискриминатор.
  5. Модель генератора обучена выводить данные, похожие на реальные данные. Однако трудно оценить сходство между сгенерированными данными и реальными данными.
  6. В генераторе и дискриминаторе архитектуры GAN модели обучаются одновременно. По мере того, как генератор становится лучше в генерации поддельных данных, дискриминатор становится лучше в обнаружении поддельных данных. Это создает постоянную конкуренцию между двумя моделями, усложняя работу генератора.
def define_generator(latent_dim):
  model = Sequential()
  # foundation for 7x7 image
  n_nodes = 128 * 7 * 7
  model.add(Dense(n_nodes, input_dim=latent_dim))
  model.add(LeakyReLU(alpha=0.2))
  model.add(Reshape((7, 7, 128)))
  # upsample to 14x14
  model.add(Conv2DTranspose(128, (4,4), strides=(2,2), padding='same'))
  model.add(LeakyReLU(alpha=0.2))
  # upsample to 28x28
  model.add(Conv2DTranspose(128, (4,4), strides=(2,2), padding='same'))
  model.add(LeakyReLU(alpha=0.2))
  model.add(Conv2D(1, (7,7), activation='sigmoid', padding='same'))
  return model
# define the size of the latent space
latent_dim = 100
# define the generator model
model = define_generator(latent_dim)
# summarize the model
model.summary()
# plot the model
plot_model(model, to_file='generator_plot.png', show_shapes=True, show_layer_names=True)

Перед тестированием модели генератора мы должны рассмотреть несколько важных шагов. В приведенном ниже фрагменте обсуждается все, но мы можем подробно рассмотреть несколько моментов.

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

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

def generate_latent_points(latent_dim, n_samples):
  # generate points in the latent space
  x_input = randn(latent_dim * n_samples)
  # reshape into a batch of inputs for the network
  x_input = x_input.reshape(n_samples, latent_dim)
  return x_input
# use the generator to generate n fake examples, with class labels
def generate_fake_samples(g_model, latent_dim, n_samples):
  # generate points in latent space
  x_input = generate_latent_points(latent_dim, n_samples)
  # predict outputs
  X = g_model.predict(x_input)
  # create ✬fake✬ class labels (0)
  y = zeros((n_samples, 1))
  return X, y
# size of the latent space
latent_dim = 100
# define the discriminator model
model = define_generator(latent_dim)
# generate samples
n_samples = 25
X, _ = generate_fake_samples(model, latent_dim, n_samples)
# plot the generated samples
fig, ax = pyplot.subplots(ncols = 4, figsize=(20,20))
for i in range(4):
  ax[i].imshow(X[i,:,:,0], cmap='gray_r')

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

Улучшение модели генератора

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

def train(g_model, d_model, gan_model, dataset, latent_dim, n_epochs=50, n_batch=256):
  bat_per_epo = int(dataset.shape[0] / n_batch)
  half_batch = int(n_batch / 2)
  # manually enumerate epochs
  for i in range(n_epochs):
  # enumerate batches over the training set
    for j in range(bat_per_epo):
    # get randomly selected ✬real✬ samples
      X_real, y_real = generate_real_samples(dataset, half_batch)
      # generate ✬fake✬ examples
      X_fake, y_fake = generate_fake_samples(g_model, latent_dim, half_batch)
      # create training set for the discriminator
      X, y = vstack((X_real, X_fake)), vstack((y_real, y_fake))
      # update discriminator model weights
      d_loss, _ = d_model.train_on_batch(X, y)
      # prepare points in latent space as input for the generator
      X_gan = generate_latent_points(latent_dim, n_batch)
      # create inverted labels for the fake samples
      y_gan = ones((n_batch, 1))
      # update the generator via the discriminator✬s error
      g_loss = gan_model.train_on_batch(X_gan, y_gan)
      # summarize loss on this batch
      print('>%d, %d/%d, d=%.3f, g=%.3f' % (i+1, j+1, bat_per_epo, d_loss, g_loss))
    if (i+1) % 10 == 0:
      summarize_performance(i, g_model, d_model, dataset, latent_dim)

latent_dim = 100
# create the discriminator
d_model = define_discriminator_model()
g_model = define_generator(latent_dim)
# create the gan
gan_model = define_gan(g_model, d_model)
# load image data
dataset = load_real_samples()
# train model
train(g_model, d_model, gan_model, dataset, latent_dim)

приведенный выше фрагмент объединяет дискриминатор и генератор, мы обучили модель для 50 эпох, но рекомендуется 100 эпох.

Генерация изображений

fig, ax = pyplot.subplots(ncols = 5,nrows=5,figsize=(20,20))
def save_plot(examples, n):
  # plot images
      for i in range(5):
        for j in range(5):
         ax[i][j].imshow(examples[(i+1)*(j+1)-1,:,:,0])

    # load model
model = load_model('generator_model_050.h5')
    # generate images
latent_points = generate_latent_points(100, 25)
    # generate images
X = model.predict(latent_points)
    # plot the result
save_plot(X, 5)

Установив cmap = «gray_r», мы можем получить рисунок ниже,

Глядя на оба сгенерированных выше изображения, мы можем с уверенностью сказать, что наш генератор способен генерировать цифры из данной случайной выборки. Несколько общих идентифицируемых цифр: 8, 0, 9 и 6. Обучение модели на 50 эпох дало такие результаты, мы можем улучшить ее, обучив определенную модель на 100 эпох для получения лучших результатов. Наконец, мы видим, что GAN — это очень мощный тип модели машинного обучения, который можно использовать для создания любого образца, для которого требуются не только изображения, но также аудио, видео, генерация текста и многое другое!

Ресурсы

  1. https://developers.google.com/machine-learning/gan#:~:text=Generative%20adversarial%20networks%20(GAN)%20являются,принадлежат%20к%20любому%20реальному%20человеку
  2. https://neptune.ai/blog/gan-loss-functions
  3. https://machinelearningmastery.com/how-to-code-generative-adversarial-network-hacks/
  4. https://medium.com/r?url=http%3A%2F%2Fwiki.pathmind.com%2Fgenerative-adversarial-network-gan
  5. https://medium.com/analytics-vidhya/introduction-to-generative-adversarial-networks-gans-852c8a29bd70