2017-11-24 6 views
0

Ich habe ein GAN-Netzwerk. Der Generator zeichnet mnist Ziffern. Es funktioniert großartig. Aber ich kann nicht verstehen, wie es weiß, welche Ziffer es zeichnen sollte. Hier ist der Generator:CNN, GAN, Wie kann der Generator wissen, welche Klasse er zeichnen sollte?

def build_generator(latent_size): 
    # we will map a pair of (z, L), where z is a latent vector and L is a 
    # label drawn from P_c, to image space (..., 1, 28, 28) 
    cnn = Sequential() 

    cnn.add(Dense(1024, input_dim=latent_size, activation='relu')) 
    cnn.add(Dense(128 * 7 * 7, activation='relu')) 
    cnn.add(Reshape((128, 7, 7))) 

    # upsample to (..., 14, 14) 
    cnn.add(UpSampling2D(size=(2, 2))) 
    cnn.add(Conv2D(256, 5, padding='same', 
        activation='relu', 
        kernel_initializer='glorot_normal')) 

    # upsample to (..., 28, 28) 
    cnn.add(UpSampling2D(size=(2, 2))) 
    cnn.add(Conv2D(128, 5, padding='same', 
        activation='relu', 
        kernel_initializer='glorot_normal')) 

    # take a channel axis reduction 
    cnn.add(Conv2D(1, 2, padding='same', 
        activation='tanh', 
        kernel_initializer='glorot_normal')) 

    # this is the z space commonly refered to in GAN papers 
    latent = Input(shape=(latent_size,)) 

    # this will be our label 
    image_class = Input(shape=(1,), dtype='int32') 

    cls = Flatten()(Embedding(num_classes, latent_size, 
           embeddings_initializer='glorot_normal')(image_class)) 

    # hadamard product between z-space and a class conditional embedding 
    h = layers.multiply([latent, cls]) 

    fake_image = cnn(h) 

    return Model([latent, image_class], fake_image) 

Die Eingabe ist eine latent-Array

noise = np.random.uniform(-1, 1, (batch_size, latent_size)) 

und die Etiketten nur zufällig erzeugt.

Also meine Frage ist. Nachdem das Netzwerk die Labels eingebettet hat. Sie sollten wie folgt aussehen

Embedding Labels

So, jetzt. Wenn ich dem Netzwerk mehr Latenz-Arrays und Labels gebe. Er wird den latent-Arrays (das Rauschen) mit der Einbettung Multiplikation (der Etiketten): Also, was ich erwarte, ist:

What I expect

Also das Netzwerk weiß, welche neue Array darstellt, welche Zahl.

aber die Ausgabe von np.multiply (Lärm, embedded_label) ist dies:

What is Reality

Wie kann das Netzwerk wissen, welche Ziffer sollte es ziehen?

######## EDIT:

Also hier ist der ganze Code. Und es funktioniert. Aber warum? Die latent_size im Code ist 100. Die latent_size in meinen Bildern ist 2, weil ich sie visualisieren wollte. Aber ich denke, es ändert nichts, wenn ich das Rauschen im 2-dimensionalen Raum oder im 100-dimensionalen Raum multipliziere. Am Ende liegen die neuen Punkte mit dem Label "1" nicht nahe bei den anderen Punkten mit dem Label "1". Das Gleiche gilt für die anderen Ziffern ("0", "1", "2", "3", ...)

#!/usr/bin/env python 
# -*- coding: utf-8 -*- 
""" 
Train an Auxiliary Classifier Generative Adversarial Network (ACGAN) on the 
MNIST dataset. See https://arxiv.org/abs/1610.09585 for more details. 

You should start to see reasonable images after ~5 epochs, and good images 
by ~15 epochs. You should use a GPU, as the convolution-heavy operations are 
very slow on the CPU. Prefer the TensorFlow backend if you plan on iterating, 
as the compilation time can be a blocker using Theano. 

Timings: 

Hardware   | Backend | Time/Epoch 
------------------------------------------- 
CPU    | TF  | 3 hrs 
Titan X (maxwell) | TF  | 4 min 
Titan X (maxwell) | TH  | 7 min 

Consult https://github.com/lukedeo/keras-acgan for more information and 
example output 
""" 
from __future__ import print_function 

from collections import defaultdict 
try: 
    import cPickle as pickle 
except ImportError: 
    import pickle 
from PIL import Image 

from six.moves import range 

import keras.backend as K 
from keras.datasets import mnist 
from keras import layers 
from keras.layers import Input, Dense, Reshape, Flatten, Embedding, Dropout 
from keras.layers.advanced_activations import LeakyReLU 
from keras.layers.convolutional import UpSampling2D, Conv2D 
from keras.models import Sequential, Model 
from keras.optimizers import Adam 
from keras.utils.generic_utils import Progbar 
import numpy as np 
import time, os 
np.random.seed(1337) 

K.set_image_data_format('channels_first') 

num_classes = 10 


def build_generator(latent_size): 
    # we will map a pair of (z, L), where z is a latent vector and L is a 
    # label drawn from P_c, to image space (..., 1, 28, 28) 
    cnn = Sequential() 

    cnn.add(Dense(1024, input_dim=latent_size, activation='relu')) 
    cnn.add(Dense(128 * 7 * 7, activation='relu')) 
    cnn.add(Reshape((128, 7, 7))) 

    # upsample to (..., 14, 14) 
    cnn.add(UpSampling2D(size=(2, 2))) 
    cnn.add(Conv2D(256, 5, padding='same', 
        activation='relu', 
        kernel_initializer='glorot_normal')) 

    # upsample to (..., 28, 28) 
    cnn.add(UpSampling2D(size=(2, 2))) 
    cnn.add(Conv2D(128, 5, padding='same', 
        activation='relu', 
        kernel_initializer='glorot_normal')) 

    # take a channel axis reduction 
    cnn.add(Conv2D(1, 2, padding='same', 
        activation='tanh', 
        kernel_initializer='glorot_normal')) 

    # this is the z space commonly refered to in GAN papers 
    latent = Input(shape=(latent_size,)) 

    # this will be our label 
    image_class = Input(shape=(1,), dtype='int32') 

    cls = Flatten()(Embedding(num_classes, latent_size, 
           embeddings_initializer='glorot_normal')(image_class)) 

    # hadamard product between z-space and a class conditional embedding 
    h = layers.multiply([latent, cls]) 

    fake_image = cnn(h) 

    return Model([latent, image_class], fake_image) 


def build_discriminator(): 
    # build a relatively standard conv net, with LeakyReLUs as suggested in 
    # the reference paper 
    cnn = Sequential() 

    cnn.add(Conv2D(32, 3, padding='same', strides=2, 
        input_shape=(1, 28, 28))) 
    cnn.add(LeakyReLU()) 
    cnn.add(Dropout(0.3)) 

    cnn.add(Conv2D(64, 3, padding='same', strides=1)) 
    cnn.add(LeakyReLU()) 
    cnn.add(Dropout(0.3)) 

    cnn.add(Conv2D(128, 3, padding='same', strides=2)) 
    cnn.add(LeakyReLU()) 
    cnn.add(Dropout(0.3)) 

    cnn.add(Conv2D(256, 3, padding='same', strides=1)) 
    cnn.add(LeakyReLU()) 
    cnn.add(Dropout(0.3)) 

    cnn.add(Flatten()) 

    image = Input(shape=(1, 28, 28)) 

    features = cnn(image) 

    # first output (name=generation) is whether or not the discriminator 
    # thinks the image that is being shown is fake, and the second output 
    # (name=auxiliary) is the class that the discriminator thinks the image 
    # belongs to. 
    fake = Dense(1, activation='sigmoid', name='generation')(features) # fake oder nicht fake 
    aux = Dense(num_classes, activation='softmax', name='auxiliary')(features) #welche klasse ist es 

    return Model(image, [fake, aux]) 

if __name__ == '__main__': 
    start_time_string = time.strftime("%Y_%m_%d_%H_%M_%S", time.gmtime()) 
    os.mkdir('history/' + start_time_string) 
    os.mkdir('images/' + start_time_string) 
    os.mkdir('acgan/' + start_time_string) 
    # batch and latent size taken from the paper 
    epochs = 50 
    batch_size = 100 
    latent_size = 100 

    # Adam parameters suggested in https://arxiv.org/abs/1511.06434 
    adam_lr = 0.00005 
    adam_beta_1 = 0.5 

    # build the discriminator 
    discriminator = build_discriminator() 
    discriminator.compile(
     optimizer=Adam(lr=adam_lr, beta_1=adam_beta_1), 
     loss=['binary_crossentropy', 'sparse_categorical_crossentropy'] 
    ) 

    # build the generator 
    generator = build_generator(latent_size) 
    generator.compile(optimizer=Adam(lr=adam_lr, beta_1=adam_beta_1), 
         loss='binary_crossentropy') 

    latent = Input(shape=(latent_size,)) 
    image_class = Input(shape=(1,), dtype='int32') 

    # get a fake image 
    fake = generator([latent, image_class]) 

    # we only want to be able to train generation for the combined model 
    discriminator.trainable = False 
    fake, aux = discriminator(fake) 
    combined = Model([latent, image_class], [fake, aux]) 

    combined.compile(
     optimizer=Adam(lr=adam_lr, beta_1=adam_beta_1), 
     loss=['binary_crossentropy', 'sparse_categorical_crossentropy'] 
    ) 

    # get our mnist data, and force it to be of shape (..., 1, 28, 28) with 
    # range [-1, 1] 
    (x_train, y_train), (x_test, y_test) = mnist.load_data() 
    x_train = (x_train.astype(np.float32) - 127.5)/127.5 
    x_train = np.expand_dims(x_train, axis=1) 

    x_test = (x_test.astype(np.float32) - 127.5)/127.5 
    x_test = np.expand_dims(x_test, axis=1) 

    num_train, num_test = x_train.shape[0], x_test.shape[0] 

    train_history = defaultdict(list) 
    test_history = defaultdict(list) 

    for epoch in range(1, epochs + 1): 
     print('Epoch {}/{}'.format(epoch, epochs)) 

     num_batches = int(x_train.shape[0]/batch_size) 
     progress_bar = Progbar(target=num_batches) 

     epoch_gen_loss = [] 
     epoch_disc_loss = [] 

     for index in range(num_batches): 
      # generate a new batch of noise 
      noise = np.random.uniform(-1, 1, (batch_size, latent_size)) 

      # get a batch of real images 
      image_batch = x_train[index * batch_size:(index + 1) * batch_size] 
      label_batch = y_train[index * batch_size:(index + 1) * batch_size] 

      # sample some labels from p_c 
      sampled_labels = np.random.randint(0, num_classes, batch_size) 

      # generate a batch of fake images, using the generated labels as a 
      # conditioner. We reshape the sampled labels to be 
      # (batch_size, 1) so that we can feed them into the embedding 
      # layer as a length one sequence 
      generated_images = generator.predict(
       [noise, sampled_labels.reshape((-1, 1))], verbose=0) 

      x = np.concatenate((image_batch, generated_images)) 
      y = np.array([1] * batch_size + [0] * batch_size) 
      aux_y = np.concatenate((label_batch, sampled_labels), axis=0) 

      # see if the discriminator can figure itself out... 
      epoch_disc_loss.append(discriminator.train_on_batch(x, [y, aux_y])) 

      # make new noise. we generate 2 * batch size here such that we have 
      # the generator optimize over an identical number of images as the 
      # discriminator 
      noise = np.random.uniform(-1, 1, (2 * batch_size, latent_size)) 
      sampled_labels = np.random.randint(0, num_classes, 2 * batch_size) 

      # we want to train the generator to trick the discriminator 
      # For the generator, we want all the {fake, not-fake} labels to say 
      # not-fake 
      trick = np.ones(2 * batch_size) 

      epoch_gen_loss.append(combined.train_on_batch(
       [noise, sampled_labels.reshape((-1, 1))], 
       [trick, sampled_labels])) 

      progress_bar.update(index + 1) 

     print('Testing for epoch {}:'.format(epoch)) 

     # evaluate the testing loss here 

     # generate a new batch of noise 
     noise = np.random.uniform(-1, 1, (num_test, latent_size)) 

     # sample some labels from p_c and generate images from them 
     sampled_labels = np.random.randint(0, num_classes, num_test) 
     generated_images = generator.predict(
      [noise, sampled_labels.reshape((-1, 1))], verbose=False) 

     x = np.concatenate((x_test, generated_images)) 
     y = np.array([1] * num_test + [0] * num_test) 
     aux_y = np.concatenate((y_test, sampled_labels), axis=0) 

     # see if the discriminator can figure itself out... 
     discriminator_test_loss = discriminator.evaluate(
      x, [y, aux_y], verbose=False) 

     discriminator_train_loss = np.mean(np.array(epoch_disc_loss), axis=0) 

     # make new noise 
     noise = np.random.uniform(-1, 1, (2 * num_test, latent_size)) 
     sampled_labels = np.random.randint(0, num_classes, 2 * num_test) 

     trick = np.ones(2 * num_test) 

     generator_test_loss = combined.evaluate(
      [noise, sampled_labels.reshape((-1, 1))], 
      [trick, sampled_labels], verbose=False) 

     generator_train_loss = np.mean(np.array(epoch_gen_loss), axis=0) 

     # generate an epoch report on performance 
     train_history['generator'].append(generator_train_loss) 
     train_history['discriminator'].append(discriminator_train_loss) 

     test_history['generator'].append(generator_test_loss) 
     test_history['discriminator'].append(discriminator_test_loss) 

     print('{0:<22s} | {1:4s} | {2:15s} | {3:5s}'.format(
      'component', *discriminator.metrics_names)) 
     print('-' * 65) 

     ROW_FMT = '{0:<22s} | {1:<4.2f} | {2:<15.2f} | {3:<5.2f}' 
     print(ROW_FMT.format('generator (train)', 
          *train_history['generator'][-1])) 
     print(ROW_FMT.format('generator (test)', 
          *test_history['generator'][-1])) 
     print(ROW_FMT.format('discriminator (train)', 
          *train_history['discriminator'][-1])) 
     print(ROW_FMT.format('discriminator (test)', 
          *test_history['discriminator'][-1])) 

     # save weights every epoch 
     generator.save_weights(
      'acgan/'+ start_time_string +'/params_generator_epoch_{0:03d}.hdf5'.format(epoch), True) 
     discriminator.save_weights(
      'acgan/'+ start_time_string +'/params_discriminator_epoch_{0:03d}.hdf5'.format(epoch), True) 

     # generate some digits to display 
     noise = np.random.uniform(-1, 1, (100, latent_size)) 

     sampled_labels = np.array([ 
      [i] * num_classes for i in range(num_classes) 
     ]).reshape(-1, 1) 

     # get a batch to display 
     generated_images = generator.predict(
      [noise, sampled_labels], verbose=0) 

     # arrange them into a grid 
     img = (np.concatenate([r.reshape(-1, 28) 
           for r in np.split(generated_images, num_classes) 
           ], axis=-1) * 127.5 + 127.5).astype(np.uint8) 

     Image.fromarray(img).save(
      'images/'+ start_time_string +'/plot_epoch_{0:03d}_generated.png'.format(epoch)) 

    pickle.dump({'train': train_history, 'test': test_history}, 
       open('history/'+ start_time_string +'/acgan-history.pkl', 'wb')) 
+0

Was ist der Wert von "latent_size"? –

+0

Also, im Code für die Ziffernzeichnung mit dem GAN. Es ist 100. Aber in den Bildern ist es 2, weil ich es visualisieren möchte. – tag

Antwort

0

Ihr Lärm ist zu groß, und hat negative Werte.

Sie sollten das Rauschen nicht multiplizieren, aber summieren (und es viel kleiner machen). Durch Multiplikation von +1 und -1 können Sie die Eingabe vollständig ändern. Dies ist der Grund dafür, dieses vollständig gestreute Bild in reality zu haben.

Wenn das Modell selbst mit diesem seltsamen Streueingang immer noch in der Lage ist, die von Ihnen gewünschte Zahl zu erkennen, verwendet es wahrscheinlich bestimmte Dimensionen des latenten Vektors mehr als seine tatsächlichen Werte.

Wenn Sie eng mit dem gestreuten Diagramm sehen, es hat einige interessante Muster wie:

  • 0 - eine vertikale Linie. Es verwendete nur eine bestimmte Dimension, um Null zu sein.
  • 4 - eine andere vertikale Linie.
  • 7 - eine horizontale Linie.
  • 3 - scheint eine Diagonale zu sein, nicht sicher.

see picture

Wenn wir ein Muster (auch in einem 2D-Diagramm versteckt tatsächliche 100 Dimensionen) sehen können, kann das Modell auch ein Muster. Dieses Muster könnte sehr offensichtlich sein, wenn wir alle 100 Dimensionen sehen könnten.

Also, Ihre Einbettung ist wahrscheinlich eine Kompensation für die wilden zufälligen Faktoren, vielleicht durch die Beseitigung der zufälligen Faktoren mit Nullen in bestimmten Gruppen von Dimensionen. Das macht die Geraden nach bestimmten Achsen. Und bestimmte Kombinationen von Null-Dimensionen gegenüber variierenden Dimensionen können ein Etikett identifizieren.

Beispiel:

  • Für das Label 0, Ihre Einbettung zu schaffen sein kann [0,0,0,0,1,1,1,1,1,1,1,1, ... ]
  • Für das Etikett 1 kann es [1,1,1,1,0,0,0,0,1,1,1,1,1 ....]
  • Für das Etikett erstellen 2, kann es sein erstellen [1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1 ...]

Dann die Zufälliger Faktor wird niemals diese Nullen ändern, und das Modell kann eine Nummer identifizieren, indem er diese Gruppen von fou überprüft r Nullen in den Beispielen.

Natürlich ist dies nur eine Annahme ... es könnte viele andere Möglichkeiten geben, wie das Modell die Zufallsfaktoren umgehen kann ... aber wenn es einen gibt, reicht es aus zu zeigen, dass es für das Modell in Ordnung ist finde es.

+0

Ich verstehe das nicht. So erzeugt meine Einbettung für Label "0" immer das gleiche Array, Array_0. Nachdem das Array_0 erstellt wurde, multipliziere ich das Rauschen. Wie kann also die Einbettung eine Kompensation für die wilden Zufallsfaktoren schaffen? Denn nachdem ich das Rauschen multipliziert habe (noise * array_0 = array_0_with_noise) ist jedes array_0_with_noise völlig anders und nicht mehr in der Nähe von array_0. BTW: Ich habe meinen Beitrag mit dem vollständigen Code bearbeitet. Vielleicht hilft das. – tag

+0

Siehe das Bild in der Antwort hinzugefügt. Wenn wir ein Muster sehen können, kann das Modell auch ein Muster sehen. Anstatt nach bestimmten Werten zu suchen, sucht Ihr Modell stattdessen nach bestimmten spezifischen Dimensionen. –

+0

Eine Möglichkeit besteht beispielsweise darin, dass die Einbettung entschieden hat, dass zum Beispiel für das Etikett 8 die ersten 10 Elemente in der latenten Dimension (100) immer Null sind. Dann ist es einfach, eine Acht zu identifizieren, indem überprüft wird, ob die ersten zehn Elemente Null sind (Nullstellen eliminieren den Zufallsfaktor multipliziert). Die anderen 90 Elemente können sein, was immer sie wollen, und die Wahrscheinlichkeit, dass viele von ihnen Null sind, ist gering. –

Verwandte Themen