Я пытаюсь обучить простой ванильный GAN на MNIST с помощью Tensorflow. Я использовал эту страницу github в качестве ссылки: https://github.com/znxlwm/tensorflow-MNIST-GAN-DCGAN/blob/master/tensorflow_MNIST_GAN.py, и в моем процессе, чтобы попытаться заставить мой GAN работать, я сделал свой код все более и более похожим на эту ссылку до точки, где он почти точная копия.
Моя проблема в том, что во время обучения, кажется, сразу же получается ситуация, когда потери дискриминатора очень низкие, а генераторы высокие, что дает ужасный результат.
Я возился с обучением дискриминатора / генератора больше, чем другой, и сейчас я на самом деле только скармливаю дискриминатору 1 MNIST di git, чтобы посмотреть, может ли он научиться именно этому (он не может). Я даже загрузил свой справочник и запустил его на своем компьютере, и всего за несколько эпох он произвел нечто похожее на числа. Я совершенно не понимаю, что здесь происходит, поскольку проблема с MNIST должна быть довольно простой.
Вот мой код:
import numpy as np
import tensorflow.compat.v1 as tf
tf.disable_v2_behavior()
import mnist
import os
import random
from PIL import Image
import math
#NOTE: heavily references this repo: https://github.com/znxlwm/tensorflow-MNIST-GAN-DCGAN/blob/master/tensorflow_MNIST_GAN.py
# Check for GPU availability
print("GPU is", "available" if tf.test.is_gpu_available() else "NOT AVAILABLE")
# Get data from MNIST
# NOTE: I did not write the code to retrieve the data, code taken from:
# https://github.com/hsjeong5/MNIST-for-Numpy
imgTrain, lblTrain, imgTest, lblTest = mnist.load()
#Hyperparameters
batchSize = 100
learningRate = 0.0002
dropoutRate = 0.3
# Dimensions of noise
zDim = 100
# Dimensions of MNIST
imageDim = 784
# Dimensions of hidden layers (backwards for descriminator)
hdim0 = 256
hdim1 = 512
hdim2 = 1024
# Generates the images
def generator(z):
# First layer
w0 = tf.Variable(tf.truncated_normal([zDim, hdim0], mean=0, stddev=0.02), name="g_w0")
b0 = tf.Variable(tf.truncated_normal([hdim0], mean=0, stddev=0.02), name="g_b0")
l0 = tf.nn.leaky_relu(tf.matmul(z,w0) + b0)
# Second layer
w1 = tf.Variable(tf.truncated_normal([hdim0, hdim1], mean=0, stddev=0.02), name="g_w1")
b1 = tf.Variable(tf.truncated_normal([hdim1], mean=0, stddev=0.02), name="g_b1")
l1 = tf.nn.leaky_relu(tf.matmul(l0,w1) + b1)
# Third layer
w2 = tf.Variable(tf.truncated_normal([hdim1, hdim2], mean=0, stddev=0.02), name="g_w2")
b2 = tf.Variable(tf.truncated_normal([hdim2], mean=0, stddev=0.02), name="g_b2")
l2 = tf.nn.leaky_relu(tf.matmul(l1,w2) + b2)
# Output
w3 = tf.Variable(tf.truncated_normal([hdim2, imageDim], mean=0, stddev=0.02), name="g_w3")
b3 = tf.Variable(tf.truncated_normal([imageDim], mean=0, stddev=0.02), name="g_b3")
output = tf.nn.tanh(tf.matmul(l2,w3) + b3)
return output
# Determines whether an image is real or fake
def discriminator(x):
# First layer
w0 = tf.Variable(tf.truncated_normal([imageDim, hdim2], mean=0, stddev=0.02), name="d_w0")
b0 = tf.Variable(tf.truncated_normal([hdim2], mean=0, stddev=0.02), name="d_b0")
l0 = tf.nn.leaky_relu(tf.matmul(x,w0) + b0)
# Second layer
w1 = tf.Variable(tf.truncated_normal([hdim2, hdim1], mean=0, stddev=0.02), name="d_w1")
b1 = tf.Variable(tf.truncated_normal([hdim1], mean=0, stddev=0.02), name="d_b1")
l1 = tf.nn.leaky_relu(tf.matmul(l0,w1) + b1)
# Third layer
w2 = tf.Variable(tf.truncated_normal([hdim1, hdim0], mean=0, stddev=0.02), name="d_w2")
b2 = tf.Variable(tf.truncated_normal([hdim0], mean=0, stddev=0.02), name="d_b2")
l2 = tf.nn.leaky_relu(tf.matmul(l1,w2) + b2)
# Output
w3 = tf.Variable(tf.truncated_normal([hdim0, 1], mean=0, stddev=0.02), name="d_w3")
b3 = tf.Variable(tf.truncated_normal([1], mean=0, stddev=0.02), name="d_b3")
output = tf.nn.sigmoid(tf.matmul(l2,w3) + b3)
return output
with tf.variable_scope("G"):
# Noise input for generator
z = tf.placeholder(tf.float32, shape=[None, zDim])
# G(z): the generated image based off of z (noise)
Gz = generator(z)
with tf.variable_scope("D") as scope:
# Image input for discriminator
x = tf.placeholder(tf.float32, shape=[None, imageDim])
# D(x): the discriminator's confidence in the real images
Dx = discriminator(x)
scope.reuse_variables()
# D(G(z)): the discriminator's confidence in the fake images
Dgz = discriminator(Gz)
# Fixes some issues
epsilon = 1e-2
# Discriminator loss
dLoss = tf.reduce_mean(-tf.log(Dx+epsilon) - tf.log(1-Dgz+epsilon))
# Generator loss
gLoss = tf.reduce_mean(-tf.log(Dgz+epsilon))
# Gets weights and biases of each network
TVars = tf.trainable_variables()
dVars = [var for var in TVars if "d_" in var.name]
gVars = [var for var in TVars if "g_" in var.name]
# Optimizer for discriminator
dOptimizer = tf.train.AdamOptimizer(learning_rate = learningRate).minimize(dLoss, var_list=dVars)
# Optimizer for generator
gOptimizer = tf.train.AdamOptimizer(learning_rate = learningRate).minimize(gLoss, var_list = gVars)
# Initialize session
sess = tf.InteractiveSession()
# Saves variable information
saver = tf.train.Saver()
# Path to save information to
savingPath = "data/MNISTGAN.ckpt"
# If the path exists restore the variables of the network
if(len(os.listdir("data"))>0):
print("Restoring variables...")
saver.restore(sess, savingPath)
# If not, initialize the variables as new
else:
print("Initializing variables...")
init = tf.global_variables_initializer()
sess.run(init)
def prepareInputVector(vector):
# Divides by 255 to squash it
returnVector = np.divide(vector, 255)
returnVector = np.multiply(np.add(returnVector, -0.5),2)
# Shapes it correctly and returns it
returnVector.shape = (1,imageDim)
return returnVector
def prepareBatch(size):
returnArray = np.zeros((size,imageDim))
for i in range(size):
returnArray[i] = prepareInputVector(imgTrain[random.randint(0, 59999)])
return returnArray
def convertArray(array):
return np.reshape(np.multiply(np.divide(np.add(array,1),2), 255), (28,28))
def generateRandomImage(name):
noise = np.random.normal(0,1, (1, zDim))
generatedArray = sess.run(Gz, feed_dict={z: noise})
imgArray = convertArray(generatedArray)
generatedImg = Image.fromarray(imgArray).convert('RGB').resize((168,168), Image.LANCZOS)
generatedImg.save("generated/"+name+".png")
epochs = 1000000
logInterval = 1
# How many epochs the discriminator trains for every 1 generator epoch
epochInterval = 1
for i in range(epochs):
# Stores loss values
dLosses = []
gLosses = []
for j in range(60000//batchSize):
noise = np.random.normal(0,1, (batchSize, zDim))
imgBatch = prepareBatch(batchSize)
currentDLoss, p,= sess.run([dLoss, dOptimizer], feed_dict={z:noise, x:imgBatch})
dLosses.append(currentDLoss)
# Generate new batch of noise
noise = np.random.normal(0,1, (batchSize, zDim))
# Optimises generator
currentGLoss, b = sess.run([gLoss, gOptimizer], feed_dict={z:noise})
# Records loss
gLosses.append(currentGLoss)
if(i%logInterval == 0):
print("Epoch:" + str(i) + "\n")
print("Generator loss:\n")
print(np.mean(gLosses))
print("\n")
print("Discriminator loss: \n")
print(str(np.mean(dLosses)))
print("\n")
savePath = saver.save(sess, savingPath)
generateRandomImage(str(i))
sess.close()
Вот некоторые из полученных мной результатов:
У меня нет графиков потерь, но потери в генераторе вырастают примерно до 4,6 после 1-2 эпох, а потери в дискриминаторе составляют около -0,02.