Друзья, если вам нужно перепечатать, пожалуйста, указывайте источник:blog.CSDN.net/Генерал сказал, о...
Ранее мы объяснили так много базовых знаний о GAN. Мы уже глубоко поняли GAN, но если вы не интегрируете вышеуказанные теоретические знания в реальный бой, вы все равно не сможете усвоить вышеуказанный контент, поэтому вы будете использовать TensorFlow для реализовать наивную GAN. (В статье используется синтаксис версии Tensorflow 1.x)
В основном мы создаем простейшую из возможных GAN, а затем обучаем ее генерировать изображения рукописных цифр, как настоящие изображения. Напишите код прямо ниже.
(1) Импорт сторонних библиотек.
import tensorflow as tf
import numpy as np
import pickle
import matplotlib.pyplot as plt
Мы используем TensorFlow для реализации сетевой архитектуры GAN и обучения построенной GAN; используем numpy для генерации случайного шума для генерации входных данных для генератора; используем pickle для сохранения переменных; и, наконец, используем matplotlib для визуализации обучения GAN. две сетевые структуры и изображения, сгенерированные GAN.
(2) Поскольку GAN должен быть обучен генерировать изображения в наборе рукописных данных MNIST, реальные изображения в наборе данных MNIST необходимо читать как реальные данные для обучения дискриминатора D. TensorFlow предоставляет метод обработки MNIST, который может использоваться для чтения данных MNIST.
from tensorflow.examples.tutorials.mnist import input_data
# 读入MNIST数据
mnist = input_data.read_data_sets('./data/MNIST_data')
img = mnist.train.images[500]
#以灰度图的形式读入
plt.imshow(img.reshape((28, 28)), cmap='Greys_r')
plt.show()
После считывания изображений MNIST каждое изображение представляется одномерной матрицей.
print(type(img))
print(img.shape)
输出如下。
<class 'numpy.ndarray'>
(784,)
PS: После версии TensorFlow 1.9 метод input_data.read_data_sets не будет загружаться автоматически, если локально нет набора данных MNIST, будет сообщено об ошибке, поэтому мы должны загрузить его заранее.
Затем определите метод получения входных данных, используя заполнитель TensorFlow для получения входных данных.
def get_inputs(real_size, noise_size):
real_img = tf.placeholder(tf.float32, [None, real_size], name='real_img')
noise_img = tf.placeholder(tf.float32, [None, noise_size], name='noise_img')
return real_img, noise_img
Затем вы можете реализовать генератор и дискриминатор.Давайте сначала посмотрим на генератор.Код выглядит следующим образом.
def generator(noise_img, n_units, out_dim, reuse=False, alpha=0.01):
'''
生成器
:paramnoise_img: 生成器生成的噪声图片
:paramn_units: 隐藏层单元数
:paramout_dim: 生成器输出的tensor的size,应该是32×32=784
:param reuse: 是否重用空间
:param alpha: leakeyReLU系数
:return:
'''
with tf.variable_scope("generator", reuse=reuse):
#全连接
hidden1 = tf.layers.dense(noise_img, n_units)
#返回最大值
hidden1 = tf.maximum(alpha * hidden1, hidden1)
hidden1 = tf.layers.dropout(hidden1, rate=0.2, training=True)
#dense:全连接
logits = tf.layers.dense(hidden1, out_dim)
outputs = tf.tanh(logits)
return logits, outputs
Можно обнаружить, что сетевая структура генератора очень проста, это просто нейронная сеть с одним скрытым слоем, а ее общая структура - входной слой → скрытый слой → выходной слой, В начале мы просто написали простейший GAN. В расширенном содержании, которое следует, сгенерируйте Структура дискриминатора и дискриминатора будет более сложной.
Кратко объясните приведенный выше код, сначала используйте tf.variable_scope для создания пространства, называемого генератором, основная цель которого состоит в том, чтобы понять, что в этом пространстве можно повторно использовать переменные, и удобно различать компоненты между различными сверточными слоями.
Затем используйте плотный метод под tf.layers, чтобы полностью соединить входной слой и скрытый слой. Модуль tf.layers предоставляет множество высокоуровневых методов инкапсуляции, с помощью которых мы можем более легко построить соответствующую структуру нейронной сети. Здесь используется плотный метод, и его роль заключается в достижении полного соединения.
Мы выбираем Leaky ReLU в качестве функции активации скрытого слоя и используем метод tf.maximum для возврата большего значения после активации Leaky ReLU.
Затем используйте метод отсева tf.layers, который состоит в том, чтобы случайным образом отбрасывать сетевой блок в нейронной сети с определенной вероятностью (то есть установить параметры сетевого блока на 0), чтобы предотвратить переоснащение, а отсев можно использовать только во время обучения. , нельзя использовать во время тестирования. Наконец, с помощью плотного метода скрытый слой и выходной слой полностью связаны, и Tanh используется в качестве функции активации выходного слоя (Tanh используется в качестве генератора функции активации для лучших результатов в эксперименте). функции Tanh составляет от -1 до 1. Другими словами, диапазон пикселей сгенерированного изображения составляет от -1 до 1, но диапазон пикселей реального изображения в наборе данных MNIST составляет от 0 до 1. Следовательно, во время обучения диапазон пикселей реального изображения должен быть скорректирован, чтобы он соответствовал сгенерированному изображению.
Функция Leakey ReLU является вариантом функции ReLU.Отличие от функции ReLU в том, что ReLU устанавливает все отрицательные значения в ноль, а Leakey ReLU умножает отрицательные значения на наклон.
Затем посмотрите на код дискриминатора.
def discirminator(img, n_units, reuse=False, alpha=0.01):
'''
判别器
:paramimg: 图片(真实图片/生成图片)
:paramn_units:
:param reuse:
:param alpha:
:return:
'''
with tf.variable_scope('discriminator', reuse=reuse):
hidden1 = tf.layers.dense(img, n_units)
hidden1 = tf.maximum(alpha * hidden1, hidden1)
logits = tf.layers.dense(hidden1, 1)
outputs = tf.sigmoid(logits)
return logits, outputs
Код реализации дискриминатора мало чем отличается от генератора, единственное отличие состоит в том, что выходной слой дискриминатора имеет только одну сетевую единицу и использует сигмоид в качестве функции активации выходного слоя.Выходное значение сигмовидной функции находится в диапазоне от 0 до 1.
После того, как генератор и дискриминатор написаны, затем записывается конкретный график расчета.Сначала выполните некоторую работу по инициализации, например, определите необходимые переменные и очистите график расчета графика по умолчанию.
img_size = mnist.train.images[0].shape[0]#真实图片大小
noise_size = 100 #噪声,Generator的初始输入
g_units = 128#生成器隐藏层参数
d_units = 128
alpha = 0.01 #leaky ReLU参数
learning_rate = 0.001 #学习速率
smooth = 0.1 #标签平滑
# 重置default graph计算图以及nodes节点
tf.reset_default_graph()
Затем мы получаем ввод реального изображения и ввод шума через метод get_inputs и передаем его в генератор и дискриминатор для обучения.Конечно, сейчас мы просто строим структуру обучения всей сети GAN.
#生成器
g_logits, g_outputs = generator(noise_img, g_units, img_size)
#判别器
d_logits_real, d_outputs_real = discirminator(real_img, d_units)
# 传入生成图片,为其打分
d_logits_fake, d_outputs_fake = discirminator(g_outputs, d_units, reuse=True)
Приведенный выше код передает шум, количество скрытых узлов слоя генератора и размер реального изображения в генератор Размер реального изображения передается, потому что генератор требуется для создания изображения того же размера. как реальный образ.
Дискриминатор сначала передает реальное изображение и узел скрытого слоя дискриминатора для оценки реального изображения, а затем использует те же параметры для обучения сгенерированного изображения оценке сгенерированного изображения.
Строится обучающая логика, затем определяются потери генератора и дискриминатора. Сначала вспомним предыдущее описание потери.Потеря дискриминатора состоит из разницы между оценкой дискриминатора реального изображения и его ожидаемой оценкой, а также разницей между оценкой дискриминатора сгенерированного изображения и его ожидаемой оценкой. Здесь самая высокая оценка равна 1, а самая низкая оценка равна 0, то есть дискриминатор надеется получить 1 балл за реальное изображение и 0 баллов за сгенерированное изображение. Потеря генератора - это, по сути, разница между распределением вероятностей сгенерированного изображения и реального изображения, которая здесь преобразуется в разницу между тем, сколько генератор ожидает, что дискриминатор наберет свое собственное сгенерированное изображение, и тем, сколько дискриминатор действительно наберет. сгенерированное изображение.
d_loss_real = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(
logits=d_logits_real, labels=tf.ones_like(d_logits_real))*(1-smooth))
d_loss_fake = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(
logits=d_logits_fake, labels=tf.zeros_like(d_logits_fake)
))
#判别器总损失
d_loss = tf.add(d_loss_real, d_loss_fake)
g_loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(
logits=d_logits_fake, labels=tf.ones_like(d_logits_fake))*(1-smooth))
При расчете потерь используйте метод tf.nn.sigmoid_cross_entropy_with_logits.Он использует сигмовидную функцию для расчета входящих параметров логитов, а затем вычисляет их кросс-энтропийные кросс-энтропийные потери.В то же время этот метод оптимизирует метод расчета кросс-энтропийных , чтобы результат не переполнялся. Из названия метода можно интуитивно понять, что он делает.
После того, как потеря определена, все, что нам нужно сделать, это минимизировать эту потерю.
# generator中的tensor
g_vars = [var for var in train_vars if var.name.startswith("generator")]
# discriminator中的tensor
d_vars = [var for var in train_vars if var.name.startswith("discriminator")]
#AdamOptimizer优化损失
d_train_opt = tf.train.AdamOptimizer(learning_rate).minimize(d_loss, var_list=d_vars)
g_train_opt = tf.train.AdamOptimizer(learning_rate).minimize(g_loss, var_list=g_vars)
Для минимизации потерь сначала получаем параметры в соответствующей сетевой структуре, то есть переменные генератора и дискриминатора, которые и являются объектами, подлежащими модификации при минимизации потерь. Здесь для минимизации потерь используется метод AdamOptimizer, который внутренне реализует алгоритм Адама, который основан на алгоритме градиентного спуска, но может динамически регулировать скорость обучения каждого параметра.
Пока что весь результат расчета примерно определен, а затем начните реализовывать конкретную логику обучения, сначала инициализируя некоторые переменные, связанные с обучением.
batch_size = 64 #每一轮训练数量
epochs = 500 #训练迭代轮数
n_sample = 25 #抽取样本数
samples = [] #存储测试样例
losses = [] #存储loss
#保存生成器变量
saver = tf.train.Saver(var_list=g_vars)
Напишите код для обучения.
with tf.Session() as sess:
# 初始化模型的参数
sess.run(tf.global_variables_initializer())
for e in range(epochs):
for batch_i in range(mnist.train.num_examples // batch_size):
batch = mnist.train.next_batch(batch_size)
#28 × 28 = 784
batch_images = batch[0].reshape((batch_size, 784))
# 对图像像素进行scale,这是因为Tanh输出的结果介于(-1,1)之间,real和fake图片共享discriminator的参数
batch_images = batch_images * 2 -1
#生成噪声图片
batch_noise = np.random.uniform(-1,1,size=(batch_size, noise_size))
#先训练判别器,再训练生成器
_= sess.run(d_train_opt, feed_dict={real_img: batch_images, noise_img:batch_noise})
_= sess.run(g_train_opt, feed_dict={noise_img:batch_noise})
#每一轮训练完后,都计算一下loss
train_loss_d = sess.run(d_loss, feed_dict={real_img:batch_images, noise_img:batch_noise})
# 判别器训练时真实图片的损失
train_loss_d_real = sess.run(d_loss_real, feed_dict={real_img:batch_images,noise_img:batch_noise})
# 判别器训练时生成图片的损失
train_loss_d_fake = sess.run(d_loss_fake, feed_dict={real_img:batch_images,noise_img:batch_noise})
# 生成器损失
train_loss_g = sess.run(g_loss, feed_dict= {noise_img: batch_noise})
print("训练轮数 {}/{}...".format(e + 1, epochs),
"判别器总损失: {:.4f}(真实图片损失: {:.4f} + 虚假图片损失: {:.4f})...".format(train_loss_d,train_loss_d_real,train_loss_d_fake),"生成器损失: {:.4f}".format(train_loss_g))
# 记录各类loss值
losses.append((train_loss_d, train_loss_d_real, train_loss_d_fake, train_loss_g))
# 抽取样本后期进行观察
sample_noise = np.random.uniform(-1, 1, size=(n_sample, noise_size))
#生成样本,保存起来后期观察
gen_samples = sess.run(generator(noise_img, g_units, img_size, reuse=True),
feed_dict={noise_img:sample_noise})
samples.append(gen_samples)
# 存储checkpoints
saver.save(sess, './data/generator.ckpt')
with open('./data/train_samples.pkl', 'wb') as f:
pickle.dump(samples,f)
В начале создается объект сеанса, а затем для обучения GAN используется двухуровневый цикл for, Первый слой указывает, сколько раундов для обучения, а второй уровень указывает размер выборки, который будет взят в каждом раунде обучения. , потому что все обучение выполняется за один раз. Эффективность реальных изображений будет относительно низкой. Общая практика состоит в том, чтобы разделить их на несколько групп, а затем выполнить несколько раундов обучения. Здесь 64 изображения - это группа.
Следующим шагом является чтение набора реальных данных.Поскольку генератор использует Tanh в качестве функции активации выходного слоя, диапазон пикселей сгенерированного изображения составляет от -1 до 1, поэтому здесь выполняется простая настройка доступа к пикселям. реального изображения, от 0 до 1. 1 становится от -1 до 1, а затем используйте универсальный метод numpy для генерации случайного шума между -1 и 1. После подготовки реальных данных и шумовых данных, вы можете закинуть их в генератор и дискриминатор.Данные будут работать в соответствии с графиком расчета, который мы разработали ранее.Стоит отметить, что сначала должен быть обучен дискриминатор, а затем генератор .
Когда все реальные изображения будут обучены в этом раунде, рассчитайте потери генератора и дискриминатора в этом раунде и запишите потери, что удобно для визуализации изменения потерь в процессе обучения GAN позже. Чтобы интуитивно чувствовать изменения генератора во время обучения ГАН, после каждого раунда обучения ГАН генератор в это время используется для генерации набора картинок и их сохранения. После того, как логика обучения написана, вы можете запустить обучающий код и вывести следующее содержимое.
Количество тренировочных раундов 1/500... Полная потеря дискриминатора: 0,0190 (потеря реального изображения: 0,0017 + потеря фальшивого изображения: 0,0173)...
Потери генератора: 4,1502
Эпохи обучения 2/500... Полная потеря дискриминатора: 1,0480 (истинная потеря изображения: 0,3772 + потеря ложного изображения: 0,6708)...
Потери генератора: 3,1548
Эпохи обучения 3/500... Полная потеря дискриминатора: 0,5315 (истинная потеря изображения: 0,3580 + потеря ложного изображения: 0,1736)...
Потери генератора: 2,8828
Эпохи обучения 4/500... Полная потеря дискриминатора: 2,9703 (истинная потеря изображения: 1,5434 + потеря ложного изображения: 1,4268)...
Потери генератора: 0,7844
Эпохи обучения 5/500... Полная потеря дискриминатора: 1,0076 (потеря реального изображения: 0,5763 + потеря фальшивого изображения: 0,4314)...
Потери генератора: 1,8176
Учебные раунды 6/500... Полная потеря дискриминатора: 0,7265 (истинная потеря изображения: 0,4558 + потеря ложного изображения: 0,2707)...
Потери генератора: 2,9691
Эпохи обучения 7/500... Полная потеря дискриминатора: 1,5635 (потеря реального изображения: 0,8336 + потеря фальшивого изображения: 0,7299)...
Потери генератора: 2,1342
Весь тренировочный процесс займет от 30 до 40 минут.