Увеличение потерь генератора MNIST GAN - PullRequest
0 голосов
/ 30 мая 2020

Я пытаюсь обучить простой ванильный 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()

Вот некоторые из полученных мной результатов:

All of the results were identical to this image

У меня нет графиков потерь, но потери в генераторе вырастают примерно до 4,6 после 1-2 эпох, а потери в дискриминаторе составляют около -0,02.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...