- Оригинальный адрес:How to Develop a 1D Generative Adversarial Network From Scratch in Keras
- Оригинальный автор:Jason Brownlee
- Перевод с:Программа перевода самородков
- Постоянная ссылка на эту статью:GitHub.com/rare earth/gold-no…
- Переводчик:TokenJan
- Корректор:haiyang-tju,todaycoder001
Как создать одномерную генеративно-состязательную сеть с нуля с помощью Keras
Генеративно-состязательные сети, или сокращенно GAN, представляет собой среду глубокого обучения для обучения мощных моделей генераторов.
Генераторная модель может использоваться для создания новых фальшивых образцов, которые, скорее всего, будут получены из существующих дистрибутивов образцов.
GAN состоят из модели генератора и модели дискриминатора. Генератор отвечает за генерацию новых выборок из домена, а дискриминатор отвечает за восприятие подлинности (сгенерированных) этих выборок. Важно отметить, что производительность модели дискриминатора используется для обновления весов модели самого дискриминатора и генератора. Это означает, что генератор не может воспринимать выборки из домена и вместо этого вносит коррективы на основе производительности дискриминатора.
Это сложная модель для понимания и обучения.
Способ лучше понять природу моделей GAN и способы их обучения — построить модель с нуля на основе простой задачи.
Простая задача одномерной функции обеспечивает хорошую среду для построения простой GAN с нуля. Это связано с тем, что как реальные, так и сгенерированные образцы могут быть построены и визуализированы для проверки того, что было изучено. Простая функция также не требует сложной модели нейронной сети, а это означает, что использование конкретных генераторов и дискриминаторов в архитектуре может быть легко понято.
В этом руководстве мы выберем простую одномерную функцию в качестве основы для построения и оценки генеративно-состязательной сети с нуля с использованием библиотеки глубокого обучения Keras.
После завершения этого урока вы узнаете:
- Преимущества построения генеративно-состязательной сети с нуля с использованием простой 1D-функции.
- Как построить отдельные модели дискриминатора и генератора, а также составную модель, которая обучает генератор, предсказывая поведение дискриминатора.
- Как субъективно оценивать сгенерированные выборки в контексте реальных данных в предметной области.
В моей новой книге GANУзнайте, как создавать DCGAN, условные GAN, Pix2Pix, CycleGAN и многое другое, а также 29 пошаговых руководств и полный исходный код.
Давайте начнем.

Как построить 1D-функцию GAN с нуля с помощью Keras Это фото сделаноChris Bambrickснято, права защищены.
Обзор учебника
Этот учебник разделен на шесть частей, а именно:
- Выберите одномерную функцию
- определить модель дискриминатора
- определить модель генератора
- Обучите модель генератора
- Оцените производительность GAN
- Полный пример обучения GAN
Выберите одномерную функцию
Первым шагом является выбор 1D-функции для моделирования.
Функция такая:
y = f(x)
в,xиyявляются входными и выходными значениями функции.
В частности, нам нужна функция, которую легко понять и построить. Это поможет установить ожидания относительно того, что должна генерировать модель, и поможет визуально проверить качество сгенерированных образцов.
Мы будем использовать простую функциюx^2; эта функция возвращает квадрат входного значения. Возможно, вы помните эту функцию из школьного курса алгебры.uфункция типа.
Мы можем определить эту функцию в Python следующим образом:
# 简单的函数
def calculate(x):
return x * x
Мы можем определить входную область как действительное число от -0,5 до 0,5, вычислить выходное значение для каждого входного значения в линейном диапазоне и построить график результатов, чтобы увидеть, как связаны вход и выход.
Полный пример ниже.
# 演示简单的 x^2 函数
from matplotlib import pyplot
# 简单的函数
def calculate(x):
return x * x
# 定义输入值
inputs = [-0.5, -0.4, -0.3, -0.2, -0.1, 0, 0.1, 0.2, 0.3, 0.4, 0.5]
# 计算输出值
outputs = [calculate(x) for x in inputs]
# 绘制结果
pyplot.plot(inputs, outputs)
pyplot.show()
Запустите пример, чтобы вычислить выходное значение для каждого входного значения и нарисовать график входных и выходных значений.
Мы видим, что значения, далекие от 0, получают большие выходные значения, тогда как значения, близкие к 0, получают меньшие выходные значения, и это поведение симметрично относительно начала координат.
Это u-график знаменитой одномерной функции X^2.

Входной и выходной график функции X^2.
Мы можем случайным образом генерировать выборки или точки из этой функции.
Этого можно достичь, генерируя случайные значения от -0,5 до 0,5 и вычисляя соответствующие им выходные значения. Повторите этот шаг много раз, чтобы получить точки выборки функции, такие как "реальный образец".
Нанесение этих выборок на график рассеяния покажет тот же U-образный график, хотя он состоит из независимых случайных выборок.
Полный пример описан ниже.
Сначала мы равномерно генерируем случайные значения в диапазоне от 0 до 1, затем смещаем их в диапазоне от -0,5 до 0,5. Затем мы вычисляем соответствующее выходное значение для каждого случайно сгенерированного входного значения и объединяем эти матрицы в один массив Numpy из n строк (100) и 2 столбцов.
# 从 X^2 中生成随机样本的例子
from numpy.random import rand
from numpy import hstack
from matplotlib import pyplot
# 从 x^2 中生成随机样本
def generate_samples(n=100):
# 在 [-0.5, 0.5] 区间内生成随机输入
X1 = rand(n) - 0.5
# 生成 X^2 (二次方)的输出
X2 = X1 * X1
# 堆叠数组
X1 = X1.reshape(n, 1)
X2 = X2.reshape(n, 1)
return hstack((X1, X2))
# 生成样本
data = generate_samples()
# 绘制样本
pyplot.scatter(data[:, 0], data[:, 1])
pyplot.show()
Выполнение этого примера сгенерирует 100 случайных входных данных, вычислит результирующие выходные данные и нарисует точечную диаграмму выборок, знакомый U-образный график.

Постройте случайно сгенерированные входные выборки и вычисленные выходные значения для функции X^2.
Мы можем использовать эту функцию в качестве отправной точки для создания реальных выборок для функции дискриминатора. В частности, выборка состоит из вектора из двух элементов: один в качестве входных данных, а другой — в качестве выходных данных нашей одномерной функции.
Мы также можем представить, как модель генератора генерирует новые выборки, и мы можем построить их, сравнивая с желаемой u-образной функцией X^2. В частности, генератор может вывести вектор из двух элементов: один в качестве входных данных и один в качестве выходных данных одномерной функции.
определить модель дискриминатора
Следующим шагом является определение модели дискриминатора.
Модель должна взять образец из нашей проблемной области, скажем, вектор из двух элементов, и вывести прогноз классификации, чтобы отличить настоящее от подделки.
Это проблема бинарной классификации.
- входить: выборка, состоящая из двух действительных чисел.
- вывод: бинарная классификация, вероятность того, что выборка верна (или ложна).
Проблема очень проста, то есть нам не нужна сложная нейронная сеть для моделирования.
Эта модель дискриминатора имеет скрытый слой с 25 нейронами, использующийФункция активации ReLUи метод инициализации He с соответствующими весами.
Выходной слой содержит нейрон, который использует сигмовидную функцию активации для бинарной классификации.
Эта модель минимизирует функцию кросс-энтропийных потерь для бинарной классификации и используетВерсия Адама о стохастическом градиентном спуске, потому что это очень эффективно.
следующееdefine_discriminator()Функция определяет и возвращает модель дискриминатора. Эта функция параметрирует ожидаемое количество входных параметров со значением по умолчанию, равным 2.
# 定义独立的判别器模型
def define_discriminator(n_inputs=2):
model = Sequential()
model.add(Dense(25, activation='relu', kernel_initializer='he_uniform', input_dim=n_inputs))
model.add(Dense(1, activation='sigmoid'))
# 编译模型
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
return model
Мы можем использовать эту функцию, чтобы определить и обобщить эту модель дискриминатора. Полный пример показан ниже.
# 定义判别器模型
from keras.models import Sequential
from keras.layers import Dense
from keras.utils.vis_utils import plot_model
# 定义独立的判别器模型
def define_discriminator(n_inputs=2):
model = Sequential()
model.add(Dense(25, activation='relu', kernel_initializer='he_uniform', input_dim=n_inputs))
model.add(Dense(1, activation='sigmoid'))
# 编译模型
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
return model
# 定义判别模型
model = define_discriminator()
# 总结模型
model.summary()
# 绘制模型
plot_model(model, to_file='discriminator_plot.png', show_shapes=True, show_layer_names=True)
Выполняя пример, он определяет и обобщает модель дискриминатора.
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
dense_1 (Dense) (None, 25) 75
_________________________________________________________________
dense_2 (Dense) (None, 1) 26
=================================================================
Total params: 101
Trainable params: 101
Non-trainable params: 0
_________________________________________________________________
Также создается график модели, и видно, что модель имеет два входа и один выход.
Уведомление: Чтобы сгенерировать этот модельный граф, вам необходимо установить библиотеки pydot и graphviz. Если у вас возникнут проблемы с установкой, вы можете импортироватьplot_modelоператоры импорта и вызовы функцийplot_modelметод закомментирован.
Схема модели генератора в генеративно-состязательной сети
Теперь мы можем начать обучение модели, используя реальные данные, помеченные 1, и случайно сгенерированные данные, помеченные 0.
Нам не нужно этого делать, но эти элементы, которые мы разработали, пригодятся позже, и они помогут нам понять, что генератор — это просто обычная модель нейронной сети.
Во-первых, мы можем обновить нашgenerate_samples()метод, названныйgenerate_real_samples(), который возвращает выходную метку реальной выборки, которая представляет собой массив единиц, где 1 — это реальная выборка.
# 生成 n 个真实样本和分类标签
def generate_real_samples(n):
# 生成 [-0.5, 0.5] 范围内的输入值
X1 = rand(n) - 0.5
# 生成输出值 X^2
X2 = X1 * X1
# 堆叠数组
X1 = X1.reshape(n, 1)
X2 = X2.reshape(n, 1)
X = hstack((X1, X2))
# 生成分类标签
y = ones((n, 1))
return X, y
Затем мы можем создать копию этого метода для создания поддельных образцов.
В этом случае мы генерируем случайные значения в диапазоне от -1 до 1 для обоих элементов выборки. Метка выходного класса для всех этих образцов равна 0.
Этот метод будет действовать как поддельная модель генератора данных.
# 生成 n 个加样本和分类标签
def generate_fake_samples(n):
# 生成 [-1, 1] 范围内的输入值
X1 = -1 + rand(n) * 2
# 生成 [-1, 1] 范围内的输出值
X2 = -1 + rand(n) * 2
# 堆叠数组
X1 = X1.reshape(n, 1)
X2 = X2.reshape(n, 1)
X = hstack((X1, X2))
# 生成分类标签
y = zeros((n, 1))
return X, y
Далее нам нужен способ обучения и оценки модели генератора.
Это можно сделать, вручную пройдя обучающие эпохи, создав половину реальных выборок и половину фальшивых выборок в каждой эпохе, а затем обновив полную партию выборок. можно использоватьtrain()метод обучения, но в этом случае мы будем напрямую использоватьtrain_on_batch()метод.
Эту модель можно сравнить с сгенерированными образцами, и мы можем создать отчет о точности классификации настоящих и поддельных образцов.
следующееtrain_discriminator()Метод реализует обучение 1000 батчей для модели, каждый батч содержит 128 отсчетов (64 поддельных отсчета и 64 реальных отсчета).
# 训练判别器模型
def train_discriminator(model, n_epochs=1000, n_batch=128):
half_batch = int(n_batch / 2)
# 手动运行 epoch
for i in range(n_epochs):
# 生成真实样本
X_real, y_real = generate_real_samples(half_batch)
# 更新模型
model.train_on_batch(X_real, y_real)
# 生成假样本
X_fake, y_fake = generate_fake_samples(half_batch)
# 更新模型
model.train_on_batch(X_fake, y_fake)
# 评估模型
_, acc_real = model.evaluate(X_real, y_real, verbose=0)
_, acc_fake = model.evaluate(X_fake, y_fake, verbose=0)
print(i, acc_real, acc_fake)
Мы можем связать их вместе и обучить модель дискриминатора на реальных и поддельных образцах.
Полный пример показан ниже.
# 定义并且加载一个判别器模型
from numpy import zeros
from numpy import ones
from numpy import hstack
from numpy.random import rand
from numpy.random import randn
from keras.models import Sequential
from keras.layers import Dense
# 定义独立的判别器模型
def define_discriminator(n_inputs=2):
model = Sequential()
model.add(Dense(25, activation='relu', kernel_initializer='he_uniform', input_dim=n_inputs))
model.add(Dense(1, activation='sigmoid'))
# 编译模型
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
return model
# 生成 n 个真实的样本和分类标签
def generate_real_samples(n):
# 生成 [-0.5, 0.5] 范围内的输入值
X1 = rand(n) - 0.5
# 生成输出 X^2
X2 = X1 * X1
# 堆叠数组
X1 = X1.reshape(n, 1)
X2 = X2.reshape(n, 1)
X = hstack((X1, X2))
# 生成分类标签
y = ones((n, 1))
return X, y
# 生成 n 个假样本和分类标签
def generate_fake_samples(n):
# 生成 [-1, 1] 范围内的输入值
X1 = -1 + rand(n) * 2
# 生成 [-1, 1] 范围内的输出值
X2 = -1 + rand(n) * 2
# 堆叠数组
X1 = X1.reshape(n, 1)
X2 = X2.reshape(n, 1)
X = hstack((X1, X2))
# 生成分类标签
y = zeros((n, 1))
return X, y
# 训练判别器模型
def train_discriminator(model, n_epochs=1000, n_batch=128):
half_batch = int(n_batch / 2)
# 手动运行 epoch
for i in range(n_epochs):
# 生成真实的样本
X_real, y_real = generate_real_samples(half_batch)
# 更新模型
model.train_on_batch(X_real, y_real)
# 生成假样本
X_fake, y_fake = generate_fake_samples(half_batch)
# 更新模型
model.train_on_batch(X_fake, y_fake)
# 评估模型
_, acc_real = model.evaluate(X_real, y_real, verbose=0)
_, acc_fake = model.evaluate(X_fake, y_fake, verbose=0)
print(i, acc_real, acc_fake)
# 定义判别器模型
model = define_discriminator()
# 加载模型
train_discriminator(model)
Запуск приведенного выше кода сгенерирует настоящие и поддельные образцы и обновит модель, затем оценит модель на тех же образцах и распечатает точность классификации.
Результаты могут отличаться, но модель быстро учится, правильно идентифицирует настоящие образцы с идеальной точностью и очень хорошо определяет поддельные образцы в диапазоне от 80% до 90%.
...
995 1.0 0.875
996 1.0 0.921875
997 1.0 0.859375
998 1.0 0.9375
999 1.0 0.8125
Процесс обучения модели дискриминатора очень интуитивно понятен. И наша цель — обучить модель генератора, а не модель дискриминатора, и именно здесь создание GAN действительно усложняется.
определить модель генератора
Следующим шагом является определение модели генератора.
Модель генератора берет точку из скрытого пространства в качестве входных данных и генерирует новую выборку, такую как входные и выходные элементы функции в виде вектора, такие как x и x^2.
Скрытая переменная — это скрытая или ненаблюдаемая переменная, а скрытое пространство — это многомерное векторное пространство, составленное из этих переменных. Мы можем определить размер скрытого пространства и его форму или распределение переменных для задачи.
Скрытое пространство не имеет смысла, пока генераторная модель не начнет обучаться и присваивать значение точкам в пространстве. После обучения точки в скрытом пространстве будут связаны с точками в выходном пространстве, то есть в сгенерированном пространстве выборки.
Мы определяем небольшое пятимерное скрытое пространство и используем стандартный подход в литературе по GAN, то есть гауссово распределение для каждой переменной в скрытом пространстве. Мы будем генерировать входные значения, взяв случайные числа из стандартного распределения Гаусса, такие как среднее значение 0 и стандартное отклонение 1.
- входить: точка в скрытом пространстве, например, вектор из пяти гауссовских случайных чисел.
- вывод: Двухэлементный вектор, представляющий выборки (x и x^2), сгенерированные для нашей функции.
Модель генератора будет такой же маленькой, как и модель дискриминатора.
Он имеет только один скрытый слой с пятью нейронами.Функция активации ReLUи метод инициализации веса He. Выходной слой имеет два нейрона, представляющих два элемента в сгенерированном векторе, и использует линейную функцию активации.
В итоге я использовал линейную функцию активации, потому что хотел, чтобы генератор выдавал действительный вектор с первым элементом в диапазоне [-0,5, 0,5], а вторым элементом в диапазоне [0,0, 0,25].
Эта модель не компилируется. Причина в том, что модель генератора не загружается напрямую.
следующееdefine_generator()Метод определяет и возвращает модель генератора.
Размер скрытого пространства параметризуется на случай, если его потребуется изменить позже, а также параметризуется размер выходного размера модели, который соответствует функции определенной модели дискриминатора.
# 定义独立的生成器模型
def define_generator(latent_dim, n_outputs=2):
model = Sequential()
model.add(Dense(15, activation='relu', kernel_initializer='he_uniform', input_dim=latent_dim))
model.add(Dense(n_outputs, activation='linear'))
return model
Мы можем обобщить эту модель, чтобы помочь лучше понять формирование входов и выходов.
Полный пример показан ниже.
# 定义生成器模型
from keras.models import Sequential
from keras.layers import Dense
from keras.utils.vis_utils import plot_model
# 定义独立的生成器模型
def define_generator(latent_dim, n_outputs=2):
model = Sequential()
model.add(Dense(15, activation='relu', kernel_initializer='he_uniform', input_dim=latent_dim))
model.add(Dense(n_outputs, activation='linear'))
return model
# 定义生成器模型
model = define_generator(5)
# 总结模型
model.summary()
# 绘制模型
plot_model(model, to_file='generator_plot.png', show_shapes=True, show_layer_names=True)
Выполняя пример, он определяет и обобщает модель генератора.
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
dense_1 (Dense) (None, 15) 90
_________________________________________________________________
dense_2 (Dense) (None, 2) 32
=================================================================
Total params: 122
Trainable params: 122
Non-trainable params: 0
_________________________________________________________________
Граф модели также генерируется, и мы видим, что модель ожидает пятикратный вектор в качестве входных данных из скрытого пространства и предсказывает двоичный вектор в качестве выходных данных.
Уведомление: Чтобы сгенерировать этот модельный граф, вам необходимо установить библиотеки pydot и graphviz. Если у вас возникнут проблемы с установкой, вы можете импортироватьplot_modelоператоры импорта и вызовы функцийplot_modelметод закомментирован.
Рисование моделей генераторов в генеративно-состязательных сетях
Мы видим, что модель берет случайный пятиэлементный вектор из скрытого пространства и выводит двухэлементный вектор для одномерной функции.
Эта модель еще мало что может. Однако мы можем использовать его, чтобы продемонстрировать, как использовать его для создания образцов. Это не обязательно, но опять же, некоторые из этих элементов могут пригодиться позже.
Первым шагом является создание новых точек в скрытом пространстве. Мы можем позвонитьМетод randn() NumPyсгенерировать из стандартного распределения Гауссаслучайный номермножество.
Затем размер массива случайных чисел может быть изменен до размера размерности выборки: это n строк по 5 элементов в каждой. следующееgenerate_latent_points()Метод реализует его и генерирует определенное количество точек в скрытом пространстве, которые можно использовать в качестве входных данных для генеративной модели.
# 在隐空间中生成点作为生成器的输入
def generate_latent_points(latent_dim, n):
# 在隐空间中生成点
x_input = randn(latent_dim * n)
# 为网络重新调整批输入样本的维度大小
x_input = x_input.reshape(n, latent_dim)
return x_input
Затем мы можем использовать эти сгенерированные точки в качестве входных данных для модели генератора для создания новых выборок, а затем построить эти выборки.
следующееgenerate_fake_samples()Метод реализует это, и в качестве параметров передаются размерность заданного генератора и скрытого пространства, а также количество точек генерации модели.
# 用生成器来生成 n 个假样本然后绘制结果
def generate_fake_samples(generator, latent_dim, n):
# 在隐空间中生成点
x_input = generate_latent_points(latent_dim, n)
# 预测输出
X = generator.predict(x_input)
# 绘制结果
pyplot.scatter(X[:, 0], X[:, 1])
pyplot.show()
Собрав все вместе, полный пример показан ниже.
# 定义和使用生成器模型
from numpy.random import randn
from keras.models import Sequential
from keras.layers import Dense
from matplotlib import pyplot
# 定义独立的生成器模型
def define_generator(latent_dim, n_outputs=2):
model = Sequential()
model.add(Dense(15, activation='relu', kernel_initializer='he_uniform', input_dim=latent_dim))
model.add(Dense(n_outputs, activation='linear'))
return model
# 生成隐空间中的点作为生成器的输入
def generate_latent_points(latent_dim, n):
# 在隐空间中生成点
x_input = randn(latent_dim * n)
# 调整网络批输入的维度大小
x_input = x_input.reshape(n, latent_dim)
return x_input
# 用生成器生成 n 个假样本来绘制结果
def generate_fake_samples(generator, latent_dim, n):
# 在隐空间中生成点
x_input = generate_latent_points(latent_dim, n)
# 预测输出
X = generator.predict(x_input)
# 绘制结果
pyplot.scatter(X[:, 0], X[:, 1])
pyplot.show()
# 隐空间的维度大小
latent_dim = 5
# 定义判别器模型
model = define_generator(latent_dim)
# 生成并绘制生成的样本
generate_fake_samples(model, latent_dim, 100)
Запуск этого примера сгенерирует 100 случайных точек из скрытого пространства, использует их в качестве входных данных для генератора и сгенерирует 100 поддельных выборок из нашей одномерной функциональной области.
Так как генератор не обучен, сгенерированные точки все "мусор", как мы думали, но мы можем представить, что когда модель будет обучена, эти точки начнут медленно двигаться в сторону целевой функции и ее типа u близко.
График рассеяния поддельных образцов, предсказанный моделью генератора.
Мы увидели, как определять и использовать модели генераторов. Нам нужно использовать модель генератора таким образом, чтобы генерировать выборки для классификации дискриминатором.
Мы не видели, как обучается модель генератора, это следующий шаг.
Обучите модель генератора
Веса в модели генератора обновляются на основе производительности модели дискриминатора.
Когда дискриминатор очень хорошо справляется с обнаружением поддельных выборок, генератор будет обновлять данные большего размера, а когда дискриминатор относительно плохо обнаруживает поддельные выборки или сбивается с толку, генератор будет обновлять меньше.
Это определяет отношения с нулевой суммой или враждебные отношения между двумя моделями.
Есть много способов сделать это с помощью Keras API, но, пожалуй, самый простой — создать новую модель, содержащую или инкапсулирующую модели генератора и дискриминатора.
В частности, новая генеративно-состязательная сетевая модель может быть определена как сложенный генератор и дискриминатор, генератор получает случайные точки в качестве входных данных в скрытом пространстве, сгенерированные выборки напрямую передаются в выборки модели дискриминатора, затем классифицируются и, наконец, выходные данные этой большой модели можно использовать для обновления весов модели генератора.
Чтобы было ясно, мы говорим не о новой сторонней модели, а просто о логически сторонней модели, которая использует слои и веса, уже определенные в отдельных моделях генератора и дискриминатора.
При различении реальных и поддельных данных задействован только дискриминатор, поэтому модель дискриминатора можно обучать отдельно от реальных и поддельных данных.
Модель генератора касается только производительности модели дискриминатора на поддельных данных. Поэтому, когда дискриминатор является частью генеративно-состязательной сетевой модели, мы пометим все слои в дискриминаторе как необучаемые, и они не будут обновлять параметры поддельных данных, чтобы предотвратить переобучение.
При обучении генератора с помощью этой комбинированной модели GAN необходимо внести еще одно важное изменение. Мы хотим, чтобы дискриминатор думал, что выборки, выдаваемые генератором, настоящие, а не фальшивые. Поэтому, когда генератор обучается как часть генеративно-состязательной сети, мы устанавливаем для помеченных сгенерированных образцов значение true (метка класса 1).
Мы можем представить, как дискриминатор классифицирует сгенерированные образцы либо как неверные (метка класса 0), либо как менее вероятные (0,3 или 0,5). Процесс обратного распространения, используемый для обновления весов модели, рассматривает это как большую ошибку, а затем обновляет веса модели (например, веса только в генераторе), чтобы исправить эту ошибку, что, в свою очередь, делает генератор лучше и делает более разумной подделку. образцы.
Давайте будем конкретными.
- входить: Точка в скрытом пространстве, например пятикратный вектор гауссовых случайных чисел.
- вывод: Двоичная классификация, вероятность того, что выборка верна (или ложна).
подdefine_gan()Метод принимает уже определенные генератор и дискриминатор в качестве параметров и создает логическую третью новую модель, содержащую эти две модели. Веса в дискриминаторе помечены как необучаемые, что влияет только на веса в генеративно-состязательной сети, а не на автономную модель дискриминатора.
Модель генеративно-состязательной сети использует ту же бинарную функцию кросс-энтропийных потерь, что и дискриминатор, и эффективнуюВерсия Адама о стохастическом градиентном спуске.
# 为更新生成器,定义合并的生成器和判别器模型
def define_gan(generator, discriminator):
# 标记判别器中的权重为不可训练
discriminator.trainable = False
# 把他们连接起来
model = Sequential()
# 加入生成器
model.add(generator)
# 加入判别器
model.add(discriminator)
# 编译模型
model.compile(loss='binary_crossentropy', optimizer='adam')
return model
Сделать дискриминатор необучаемым — хитрый трюк в Keras API.
Когда модель скомпилирована,trainableсвойства влияют на это. Модель дискриминатора скомпилирована с обучаемыми слоями, поэтому вызовtrain_on_batch()для обновления независимых моделей он также обновляет весовые модели в этих слоях.
Модель дискриминатора помечается как необучаемая, добавляется к модели генеративно-состязательной сети, а затем компилируется. В этой модели, вызываяtrain_on_batch()При обновлении модели генеративно-состязательной сети веса в модели дискриминатора не поддаются обучению и не могут быть изменены.
Это поведение описано в документации Keras API:
Полный пример создания дискриминатора, генератора и комбинированной модели приведен ниже.
# 演示创建生成对抗网络的三种模型
from keras.models import Sequential
from keras.layers import Dense
from keras.utils.vis_utils import plot_model
# 定义独立的判别器模型
def define_discriminator(n_inputs=2):
model = Sequential()
model.add(Dense(25, activation='relu', kernel_initializer='he_uniform', input_dim=n_inputs))
model.add(Dense(1, activation='sigmoid'))
# 编译模型
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
return model
# 定义独立的生成器模型
def define_generator(latent_dim, n_outputs=2):
model = Sequential()
model.add(Dense(15, activation='relu', kernel_initializer='he_uniform', input_dim=latent_dim))
model.add(Dense(n_outputs, activation='linear'))
return model
# 定义合并的生成器和判别器模型,为了更新生成器
def define_gan(generator, discriminator):
# 标记判别器模型中的权重为不可训练
discriminator.trainable = False
# 连接它们
model = Sequential()
# 加入生成器
model.add(generator)
# 加入判别器
model.add(discriminator)
# 编译模型
model.compile(loss='binary_crossentropy', optimizer='adam')
return model
# 隐空间的维度大小
latent_dim = 5
# 创建判别器
discriminator = define_discriminator()
# 创建生成器
generator = define_generator(latent_dim)
# 创建生成对抗网络
gan_model = define_gan(generator, discriminator)
# 总结生成对抗网络模型
gan_model.summary()
# 绘制生成对抗网络模型
plot_model(gan_model, to_file='gan_plot.png', show_shapes=True, show_layer_names=True)
При выполнении примера сначала создается сводка комбинированной модели.
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
sequential_2 (Sequential) (None, 2) 122
_________________________________________________________________
sequential_1 (Sequential) (None, 1) 101
=================================================================
Total params: 223
Trainable params: 122
Non-trainable params: 101
_________________________________________________________________
Также создается граф модели, и мы видим, что модель ожидает пятикратную точку в качестве входных данных в скрытом пространстве и предсказывает метку класса для выходных данных.
Уведомление: Чтобы сгенерировать этот модельный граф, вам необходимо установить библиотеки pydot и graphviz. Если у вас возникнут проблемы с установкой, вы можете импортироватьplot_modelоператоры импорта и вызовы функцийplot_modelметод закомментирован.

График комбинированной модели генератора и дискриминатора в генеративно-состязательных сетях
Обучение комбинированной модели состоит из использованияgenerate_latent_points()метод генерирует пакет точек в скрытом пространстве и метки с классом = 1, вызовtrain_on_batch()метод.
следующееtrain_gan()Метод демонстрирует этот процесс, хотя он не очень интересен, поскольку в каждую эпоху обновляется только генератор, а дискриминатор сохраняет веса модели по умолчанию.
# 训练组合模型
def train_gan(gan_model, latent_dim, n_epochs=10000, n_batch=128):
# 手动遍历 epoch
for i in range(n_epochs):
# 为生成器准备隐空间中的点作为输入
x_gan = generate_latent_points(latent_dim, n_batch)
# 为假样本创建反标签
y_gan = ones((n_batch, 1))
# 通过判别器的误差更新生成器
gan_model.train_on_batch(x_gan, y_gan)
Сначала нам нужно обновить модель дискриминатора реальными и поддельными образцами, а затем обновить генератор комбинированной моделью.
Это требует объединения определений, определенных в дискриминаторе.train_discriminator()метод и определен вышеtrain_gan()элемент в методе. также нуженgenerate_fake_samples()Метод использует модель генератора для создания поддельных выборок вместо генерации случайных чисел.
Полный метод обучения для обновления модели дискриминатора и генератора (путем объединения моделей) показан ниже.
# 训练生成器和判别器
def train(g_model, d_model, gan_model, latent_dim, n_epochs=10000, n_batch=128):
# 将一半 batch 数量用来更新判别器
half_batch = int(n_batch / 2)
# 手动遍历 epoch
for i in range(n_epochs):
# 准备真实样本
x_real, y_real = generate_real_samples(half_batch)
# 准备假样本
x_fake, y_fake = generate_fake_samples(g_model, latent_dim, half_batch)
# 更新判别器
d_model.train_on_batch(x_real, y_real)
d_model.train_on_batch(x_fake, y_fake)
# 准备隐空间中的点作为生成器中的输入
x_gan = generate_latent_points(latent_dim, n_batch)
# 为假样本创建反标签
y_gan = ones((n_batch, 1))
# 通过判别器的误差更新生成器
gan_model.train_on_batch(x_gan, y_gan)
У нас есть почти все необходимое для построения генеративно-состязательной сети с использованием 1D-функций.
Остальное - оценка модели.
Оценить производительность генеративно-состязательных сетей
Вообще говоря, не существует объективного способа оценить производительность генеративно-состязательных сетевых моделей.
В этом конкретном примере мы можем разработать объективную меру для сгенерированных выборок, поскольку мы знаем потенциально истинную входную область и целевую функцию и можем вычислить объективную меру ошибки.
Однако в этом руководстве мы не будем вычислять это объективное значение ошибки. Вместо этого мы будем использовать субъективный подход, который используется в большинстве генеративно-состязательных сетевых приложений. В частности, мы будем использовать генератор для генерации новых образцов, а затем проверим, насколько они близки к реальным образцам в предметной области.
Во-первых, мы можем использовать ранее созданный раздел дискриминатораgenerate_real_samples()способ создания новых образцов. Построение графика рассеяния с этими образцами дает знакомую U-образную целевую функцию.
# 生成 n 个真实样本和类标签
def generate_real_samples(n):
# 生成 [-0.5, 0.5] 范围内的输入值
X1 = rand(n) - 0.5
# 生成输出值 X^2
X2 = X1 * X1
# 堆叠数组
X1 = X1.reshape(n, 1)
X2 = X2.reshape(n, 1)
X = hstack((X1, X2))
# 生成类标签
y = ones((n, 1))
return X, y
Затем мы можем использовать модель генератора для создания такого же количества поддельных образцов.
Это сначала необходимо создать через раздел генератора выше.generate_latent_points()способ генерировать одинаковое количество точек в скрытом пространстве. Эти точки можно передать в модель генератора и сгенерировать выборки, которые можно нанести на ту же диаграмму рассеяния.
# 在隐空间中生成点作为生成器的输入
def generate_latent_points(latent_dim, n):
# 在隐空间中生成点
x_input = randn(latent_dim * n)
# 为网络调整一个 batch 输入的维度大小
x_input = x_input.reshape(n, latent_dim)
return x_input
следующееgenerate_fake_samples()Метод генерирует эти поддельные образцы и связанную с ними метку класса 0, которая будет полезна позже.
# 用生成器生成 n 个假样本和类标签
def generate_fake_samples(generator, latent_dim, n):
#在隐空间中生成点
x_input = generate_latent_points(latent_dim, n)
# 预测输出
X = generator.predict(x_input)
# 创建类标签
y = zeros((n, 1))
return X, y
Обе выборки нанесены на один и тот же график, чтобы их можно было сравнить напрямую, субъективно увидев, охвачены ли одни и те же входные и выходные области и правильно ли очерчена желаемая форма целевой функции.
следующееsummarize_performance()Метод можно вызвать в любой момент во время обучения, и он может рисовать реальные и сгенерированные диаграммы рассеяния, чтобы получить приблизительное представление о текущих возможностях генеративной модели.
# 绘制真假点
def summarize_performance(generator, latent_dim, n=100):
# 准备真实样本
x_real, y_real = generate_real_samples(n)
# 准备假样本
x_fake, y_fake = generate_fake_samples(generator, latent_dim, n)
# 绘制真假数据点的散点图
pyplot.scatter(x_real[:, 0], x_real[:, 1], color='red')
pyplot.scatter(x_fake[:, 0], x_fake[:, 1], color='blue')
pyplot.show()
В качестве альтернативы нас также может заинтересовать производительность модели дискриминатора.
В частности, нас интересует способность модели дискриминатора правильно различать настоящие и поддельные образцы. Хорошая модель генератора должна сбивать с толку модель дискриминатора, что приводит к точности классификации, близкой к 50%, для реальных и поддельных образцов.
мы можем обновитьsummarize_performance()метод, так что он получает в качестве параметров дискриминатор и номер текущей эпохи и формирует отчет о точности истинных и ложных отсчетов.
# 评估判别器并且绘制真假点
def summarize_performance(epoch, generator, discriminator, latent_dim, n=100):
# 准备真实样本
x_real, y_real = generate_real_samples(n)
# 在真实样本上评估判别器
_, acc_real = discriminator.evaluate(x_real, y_real, verbose=0)
# 准备假样本
x_fake, y_fake = generate_fake_samples(generator, latent_dim, n)
# 在假样本上评估判别器
_, acc_fake = discriminator.evaluate(x_fake, y_fake, verbose=0)
# 总结判别器性能
print(epoch, acc_real, acc_fake)
# 绘制真假数据的散点图
pyplot.scatter(x_real[:, 0], x_real[:, 1], color='red')
pyplot.scatter(x_fake[:, 0], x_fake[:, 1], color='blue')
pyplot.show()
Этот метод можно периодически вызывать во время обучения.
Например, если мы итеративно обучаем модель 10 000 раз, проверяйте производительность этой модели каждые 2 000 итераций.
мы можем пройтиn_evalстроковый параметр для параметризации частоты проверок, и после определенного количества итерацийtrain()вызов методаsummarize_performance()метод.
после обновленияtrain()Метод показан ниже.
# 训练生成器和判别器
def train(g_model, d_model, gan_model, latent_dim, n_epochs=10000, n_batch=128, n_eval=2000):
# 用一半的 batch 数量来更新判别器
half_batch = int(n_batch / 2)
# 手动遍历 epoch
for i in range(n_epochs):
# 准备真实样本
x_real, y_real = generate_real_samples(half_batch)
# 准备假样本
x_fake, y_fake = generate_fake_samples(g_model, latent_dim, half_batch)
# 更新判别器
d_model.train_on_batch(x_real, y_real)
d_model.train_on_batch(x_fake, y_fake)
# 准备隐空间中的点作为生成器的输入
x_gan = generate_latent_points(latent_dim, n_batch)
# 为假样本创建反标签
y_gan = ones((n_batch, 1))
# 通过判别器的误差更新生成器
gan_model.train_on_batch(x_gan, y_gan)
# 每 n_eval epoch 评估模型
if (i+1) % n_eval == 0:
summarize_performance(i, g_model, d_model, latent_dim)
Полный пример обучения генеративно-состязательной сети
Теперь у нас есть все необходимое для обучения и оценки генеративно-состязательных сетей для 1D-функций.
Полный пример показан ниже.
# 在一个一维函数上训练一个生成对抗网络
from numpy import hstack
from numpy import zeros
from numpy import ones
from numpy.random import rand
from numpy.random import randn
from keras.models import Sequential
from keras.layers import Dense
from matplotlib import pyplot
# 定义独立的判别器模型
def define_discriminator(n_inputs=2):
model = Sequential()
model.add(Dense(25, activation='relu', kernel_initializer='he_uniform', input_dim=n_inputs))
model.add(Dense(1, activation='sigmoid'))
# 编译模型
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
return model
# 定义独立的生成器模型
def define_generator(latent_dim, n_outputs=2):
model = Sequential()
model.add(Dense(15, activation='relu', kernel_initializer='he_uniform', input_dim=latent_dim))
model.add(Dense(n_outputs, activation='linear'))
return model
# 定义合并的生成器和判别器模型,来更新生成器
def define_gan(generator, discriminator):
# 将判别器的权重设为不可训练
discriminator.trainable = False
# 连接它们
model = Sequential()
# 加入生成器
model.add(generator)
# 加入判别器
model.add(discriminator)
# 编译模型
model.compile(loss='binary_crossentropy', optimizer='adam')
return model
# 生成 n 个真实样本和类标签
def generate_real_samples(n):
# 生成 [-0.5, 0.5] 范围内的输入值
X1 = rand(n) - 0.5
# 生成输出值 X^2
X2 = X1 * X1
# 堆叠数组
X1 = X1.reshape(n, 1)
X2 = X2.reshape(n, 1)
X = hstack((X1, X2))
# 生成类标签
y = ones((n, 1))
return X, y
# 生成隐空间中的点作为生成器的输入
def generate_latent_points(latent_dim, n):
# 在隐空间中生成点
x_input = randn(latent_dim * n)
# 为网络调整一个 batch 输入的维度大小
x_input = x_input.reshape(n, latent_dim)
return x_input
# 用生成器生成 n 个假样本和类标签
def generate_fake_samples(generator, latent_dim, n):
# 在隐空间中生成点
x_input = generate_latent_points(latent_dim, n)
# 预测输出值
X = generator.predict(x_input)
# 创建类标签
y = zeros((n, 1))
return X, y
# 评估判别器并且绘制真假点
def summarize_performance(epoch, generator, discriminator, latent_dim, n=100):
# 准备真实样本
x_real, y_real = generate_real_samples(n)
# 在真实样本上评估判别器
_, acc_real = discriminator.evaluate(x_real, y_real, verbose=0)
# 准备假样本
x_fake, y_fake = generate_fake_samples(generator, latent_dim, n)
# 在假样本上评估判别器
_, acc_fake = discriminator.evaluate(x_fake, y_fake, verbose=0)
# 总结判别器性能
print(epoch, acc_real, acc_fake)
# 绘制真假数据的散点图
pyplot.scatter(x_real[:, 0], x_real[:, 1], color='red')
pyplot.scatter(x_fake[:, 0], x_fake[:, 1], color='blue')
pyplot.show()
# 训练生成器和判别器
def train(g_model, d_model, gan_model, latent_dim, n_epochs=10000, n_batch=128, n_eval=2000):
# 用一半的 batch 数量来训练判别器
half_batch = int(n_batch / 2)
# 手动遍历 epoch
for i in range(n_epochs):
# 准备真实样本
x_real, y_real = generate_real_samples(half_batch)
# 准备假样本
x_fake, y_fake = generate_fake_samples(g_model, latent_dim, half_batch)
# 更新判别器
d_model.train_on_batch(x_real, y_real)
d_model.train_on_batch(x_fake, y_fake)
# 在隐空间中准备点作为生成器的输入
x_gan = generate_latent_points(latent_dim, n_batch)
# 为假样本创建反标签
y_gan = ones((n_batch, 1))
# 通过判别器的误差更新生成器
gan_model.train_on_batch(x_gan, y_gan)
# 为每 n_eval epoch 模型做评估
if (i+1) % n_eval == 0:
summarize_performance(i, g_model, d_model, latent_dim)
# 隐空间的维度
latent_dim = 5
# 创建判别器
discriminator = define_discriminator()
# 创建生成器
generator = define_generator(latent_dim)
# 创建生成对抗网络
gan_model = define_gan(generator, discriminator)
# 训练模型
train(generator, discriminator, gan_model, latent_dim)
Выполнение этого примера создаст отчет о производительности модели и построит диаграмму рассеяния каждые 2000 пакетов обучения.
Ваши собственные результаты могут отличаться из-за случайного характера алгоритма обучения и характера самой генеративной модели.
Мы видим, что тренировочный процесс относительно нестабилен. Первый столбец — количество итераций, второй столбец — точность классификации дискриминатора для реальных выборок, а третий столбец — точность классификации дискриминатора для сгенерированных (поддельных) выборок.
В этом случае мы видим, что дискриминатор все еще сильно запутался в реальных образцах, и производительность идентификации поддельных образцов также сильно отличается.
1999 0.45 1.0
3999 0.45 0.91
5999 0.86 0.16
7999 0.6 0.41
9999 0.15 0.93
Для простоты я опущу пять созданных точечных диаграмм, мы рассмотрим только две из них.
Первый график, созданный после 2000 итераций, показывает сравнение реальных (красный) и поддельных (синий) образцов. Сначала модель работала не очень хорошо, генерируя точки только в положительной области ввода, хотя функциональное соотношение было правильным.

Графики рассеяния реальных и сгенерированных выборок для целевой функции после 2000 итераций.
Вторая диаграмма рассеяния — это сравнение реальных (красный) и поддельных (синий) образцов после 10 000 итераций.
Здесь мы видим, что генеративная модель генерирует реалистичные выборки, входной домен находится в правильном диапазоне от -0,5 до 0,5, а выходные значения показывают приблизительную функциональную зависимость X^2.

Графики рассеяния реальных и сгенерированных выборок для целевой функции после 10 000 итераций.
расширять
В этом разделе перечислены некоторые идеи, которые вы, возможно, захотите изучить вне учебника.
- Архитектура модели: Экспериментируйте с другими архитектурами моделей дискриминатора и генератора, такими как большее или меньшее количество нейронов, слоев и альтернативные функции активации, такие как дырявый ReLU.
- шкала данных: Используйте другие функции активации, такие как гиперболический тангенс (tanh) и любой желаемый размер обучающих данных.
- другие целевые функции: Используйте другую целевую функцию, например, простую синусоиду, распределение Гаусса, другое квадратное уравнение или даже мультимодальную полиномиальную функцию.
Если вы исследуете эти расширения, я хотел бы услышать о них. Оставьте свои выводы в комментариях ниже.
Расширенное чтение
В этом разделе содержится больше ресурсов по этой теме, если вы хотите копнуть глубже.
API
- Keras API
- Как я могу «заморозить» слои Keras?
- MatplotLib API
- numpy.random.rand API
- numpy.random.randn API
- numpy.zeros API
- numpy.ones API
- numpy.hstack API
Суммировать
В этом руководстве вы узнали, как создать генеративно-состязательную сеть с нуля, используя одномерную функцию.
В частности, вы узнали:
- Преимущества построения генеративно-состязательной сети с нуля с использованием простой 1D-функции.
- Как построить отдельные модели дискриминатора и генератора, а также комбинированную модель, которая обучает генератор, предсказывая поведение дискриминатора.
- Как субъективно оценивать сгенерированные выборки в контексте реальных данных в предметной области.
У вас есть вопросы? Пишите свои вопросы в комментариях ниже, и я постараюсь на них ответить.
Если вы обнаружите ошибки в переводе или в других областях, требующих доработки, добро пожаловать наПрограмма перевода самородковВы также можете получить соответствующие бонусные баллы за доработку перевода и PR. начало статьиПостоянная ссылка на эту статьюЭто ссылка MarkDown этой статьи на GitHub.
Программа перевода самородковэто сообщество, которое переводит высококачественные технические статьи из Интернета сНаггетсДелитесь статьями на английском языке на . Охват контентаAndroid,iOS,внешний интерфейс,задняя часть,блокчейн,продукт,дизайн,искусственный интеллектЕсли вы хотите видеть более качественные переводы, пожалуйста, продолжайте обращать вниманиеПрограмма перевода самородков,официальный Вейбо,Знай колонку.