100-строчный код Tensorflow и эквивалент Keras не дают одинакового результата, результат Tensorflow не сходится, почему? - PullRequest
1 голос
/ 29 января 2020

Я больше привык к Keras, чем к Tensorflow, но хотел бы перейти на Tensorflow, так как он кажется более мощным.

Я провожу эксперименты с базовым c GAN, код которого предоставляется для обоих TF (я использую версию 1.15.2) и Keras (2.2.5), ниже. GAN учится генерировать 2D точки, соответствующие 2D кривой, f (x) = x ^ 2. Каждая точка рассматривается как двумерный вектор «индивидуально», (x, y).

Следующие 2 python программы компилируются и запускаются «из коробки» и дают точно такой же результат. Однако версия TF вообще не сходится должным образом.

Я перепробовал все, что мог, чтобы понять, что идет не так, безрезультатно. Очевидно, что я что-то упускаю.

Версия TF в значительной степени основана на официальном руководстве GAN, расположенном здесь: https://www.tensorflow.org/tutorials/generative/dcgan

Я также предоставляю код в 2 ссылки для вставки:

Версия Keras: https://pastebin.com/N26e3hWh Версия Tensorflow: https://pastebin.com/9ebmSyJB

Версия Keras:

import numpy as np
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
import keras.backend as K
from matplotlib import pyplot

class GanPointGraph_Keras(object):

    def __init__(self):
        self.latent_dim = 5
        self.discriminator = self.define_discriminator()
        self.generator = self.define_generator(self.latent_dim)
        self.gan_model = self.define_gan(self.generator, self.discriminator)

    def define_discriminator(self, 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'])
        print(K.eval(model.optimizer.lr))
        return model

    def define_generator(self, 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(self, generator, discriminator):
        discriminator.trainable = False
        model = Sequential()
        model.add(generator)
        model.add(discriminator)
        model.compile(loss='binary_crossentropy', optimizer='adam')
        return model

    def generate_latent_points(self, n):
        x_input = randn(self.latent_dim * n)
        x_input = x_input.reshape(n, self.latent_dim)
        return x_input

    def generate_fake_samples(self, n):
        x_input = self.generate_latent_points(n)
        X = self.generator.predict(x_input)
        return X

    def generate_real_samples(self, n):
        X1 = rand(n) - 0.5
        X2 = X1 * X1
        X1 = X1.reshape(n, 1)
        X2 = X2.reshape(n, 1)
        X = hstack((X1, X2))    
        return X

    def train(self):
        n_batch = 128
        half_batch = int(n_batch / 2)
        x_real = self.generate_real_samples(half_batch)
        y_real = ones((half_batch, 1))
        x_fake = self.generate_fake_samples(half_batch)
        y_fake = zeros((half_batch, 1))
        self.discriminator.train_on_batch(x_real, y_real)
        self.discriminator.train_on_batch(x_fake, y_fake)
        x_gan = self.generate_latent_points(n_batch)
        y_gan = ones((n_batch, 1))
        self.gan_model.train_on_batch(x_gan, y_gan)

if __name__ == "__main__":
    g = GanPointGraph_Keras();

    for epoch in range(10000):
        print('Epoch', epoch)
        g.train()
        if epoch % 1000 == 0:
            g_objects = g.generate_fake_samples(100)
            r_objects = g.generate_real_samples(100)

            pyplot.clf()
            pyplot.title('Keras iteration ' + str(epoch))
            pyplot.scatter([i[0] for i in r_objects], [i[1] for i in r_objects], c='black')
            pyplot.scatter([i[0] for i in g_objects], [i[1] for i in g_objects], c='red')
            pyplot.show()

Версия Tensorflow:

import tensorflow as tf
tf.enable_eager_execution() # if using TF 1.15.x
from tensorflow.keras import layers

import numpy as np
from numpy.random import rand
from numpy import hstack

from matplotlib import pyplot

class GanPointGraph(object):

    def __init__(self):
        self.latent_dim = 5
        self.generator = self.make_generator()
        self.discriminator = self.make_discriminator()

        self.cross_entropy = tf.keras.losses.BinaryCrossentropy(from_logits=True)        
        self.generator_optimizer = tf.keras.optimizers.Adam(learning_rate=0.001)
        self.discriminator_optimizer = tf.keras.optimizers.Adam(learning_rate=0.001)

    def make_generator(self):
        model = tf.keras.Sequential()
        model.add(layers.Dense(15, activation='relu', input_dim=self.latent_dim))
        model.add(layers.Dense(2))
        return model

    def make_discriminator(self):
        model = tf.keras.Sequential()
        model.add(layers.Dense(25, activation='relu', input_dim=2))
        model.add(layers.Dense(1, activation='sigmoid')) # (-infinity, infinity) -> (0, 1)
        return model

    def generator_loss(self, fake_output):
        #return self.cross_entropy(tf.ones_like(fake_output), fake_output)
        return tf.reduce_mean(tf.math.log(1-fake_output))

    def discriminator_loss(self, real_output, fake_output):
        #real_loss = self.cross_entropy(tf.ones_like(real_output), real_output)
        #fake_loss = self.cross_entropy(tf.zeros_like(fake_output), fake_output)
        #total_loss = real_loss + fake_loss
        #return total_loss
        loss_real = tf.reduce_mean(-tf.math.log(real_output))
        loss_fake = tf.reduce_mean(-tf.math.log(1-fake_output))
        D_loss = loss_real + loss_fake
        return D_loss

    def generate_real_samples(self, n):
        X1 = rand(n) - 0.5
        X2 = X1 * X1
        X1 = X1.reshape(n, 1)
        X2 = X2.reshape(n, 1)
        x_train = hstack((X1, X2))
        return x_train

    def generate_fake_samples(self, n):
        z_sample = np.random.normal(0, 1.0, size=[n, self.latent_dim]).astype(np.float32)
        return self.generator(z_sample, training=False).numpy()

    def train(self):
        images = self.generate_real_samples(128);
        noise = tf.random.normal([images.shape[0], self.latent_dim])

        with tf.GradientTape() as gen_tape, tf.GradientTape() as disc_tape:
            generated_images = self.generator(noise, training=True)

            real_output = self.discriminator(images, training=True)
            fake_output = self.discriminator(generated_images, training=True)

            gen_loss = self.generator_loss(fake_output)
            disc_loss = self.discriminator_loss(real_output, fake_output)

        gradients_of_generator = gen_tape.gradient(gen_loss, self.generator.trainable_variables)
        gradients_of_discriminator = disc_tape.gradient(disc_loss, self.discriminator.trainable_variables)

        self.generator_optimizer.apply_gradients(zip(gradients_of_generator, self.generator.trainable_variables))
        self.discriminator_optimizer.apply_gradients(zip(gradients_of_discriminator, self.discriminator.trainable_variables))

if __name__ == "__main__":
    g = GanPointGraph();

    for epoch in range(10000):
        print('Epoch', epoch)
        g.train()
        if epoch % 1000 == 0:
            g_objects = g.generate_fake_samples(100)
            r_objects = g.generate_real_samples(100)

            pyplot.clf()
            pyplot.title('Tensorflow iteration ' + str(epoch))
            pyplot.scatter([i[0] for i in r_objects], [i[1] for i in r_objects], c='black')
            pyplot.scatter([i[0] for i in g_objects], [i[1] for i in g_objects], c='red')
            pyplot.show()

Если вы запустите программу, вы увидите, что версия Keras сходится довольно надежно. Версия TF просто «танцует» вокруг решения, очень часто получая довольно плохие результаты, даже после большого количества эпох.

Что я делаю не так в Tensorflow?

Спасибо,

F C

1 Ответ

0 голосов
/ 13 апреля 2020

Tensorflow и Керас код не даст такой же результат из-за рандомизации .
Однако, если модель правильная, и одинаковые должны оба приближаться к одинаковым результатам (в данном случае сходящиеся ).

Я выполнил предоставленный вами код в среде Google Colab оба предоставленных вами кода сходятся после Epochs > 6000.

Кроме того, если вы хотите портировать модель Native Keras import keras, вы можно просто сделать это, просто добавив Tensorflow перед Keras import tensorflow.keras, это официально поддерживается в версии Tensorflow> 2.0.0 (последняя версия 2.1.0).

Модифицируя импорт вашего кода Native Keras , вы можете запустить его в TensorFlow 2.1.0 .

import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
import tensorflow.keras.backend as K

You могли бы узнать больше о TensorFlow Migration в этой ссылке .

...