【问题标题】:CNN, GAN, How can the Generator know, what class it should draw?CNN、GAN、生成器怎么知道,它应该画什么类?
【发布时间】:2017-11-24 16:50:35
【问题描述】:

我有一个 GAN 网络。生成器正在绘制 mnist 数字。它工作得很好。但我不明白它是怎么知道的,它应该画哪个数字。 这是生成器:

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)

输入是一个潜在数组

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

标签只是随机生成的。

所以我的问题是。在网络嵌入标签之后。它们应该是这样的

那么,现在。如果我给网络更多的潜在数组和标签。他将潜在数组(噪声)与嵌入(标签)相乘: 所以我期望的是:

所以网络知道,什么新数组代表什么数字。

但是 np.multiply(noise,embedded_label) 的输出是这样的:

那么网络怎么知道它应该画什么数字呢?

编辑:

所以这是整个代码。它有效。但为什么? 代码中的latent_size为100。我的图片中的latent_size为2,因为我想将它们可视化。但我认为它不会改变任何事情,如果我将 2 维空间或 100 维空间中的噪声相乘。最后,标签为“1”的新点不靠近标签为“1”的其他点。其他数字相同("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'))

【问题讨论】:

  • “latent_size”的值是多少?
  • 因此,在使用 GAN 进行数字绘图的代码中。是 100。但在图片中是 2,因为我想将其可视化。

标签: python keras conv-neural-network embedding


【解决方案1】:

您的噪音太大,并且具有负值。

您不应将噪声相乘,而应将其相加(并使其更小)。 通过将 +1 和 -1 相乘,您可以完全改变输入。这就是在reality 中拥有完全分散的图像的原因。

如果即使使用那个奇怪的分散输入,模型仍然能够识别您的意思的数字,那么它可能使用的潜在向量的某些维度超过了它的实际值。

如果你仔细观察散点图,它有一些有趣的模式,例如:

  • 0 - 一条垂直线。它仅使用某个维度为零。
  • 4 - 另一条垂直线。
  • 7 - 一条水平线。
  • 3 - 似乎是对角线,不确定。

如果我们可以看到一种模式(即使在隐藏实际 100 维的二维图中),模型也可以看到一种模式。如果我们能看到所有 100 个维度,这种模式可能会非常明显。

因此,您的嵌入可能是通过消除某些维度组中为零的随机因素来为野生随机因素创造补偿。这使得直线遵循某些轴。零维度与不同维度的某些组合可能会识别标签。

例子:

  • 对于标签 0,您的嵌入可能会创建 [0,0,0,0,1,1,1,1,1,1,1,1,...]
  • 对于标签 1,它可能正在创建 [1,1,1,1,0,0,0,0,1,1,1,1,1....]
  • 对于标签 2,它可能正在创建 [1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1...]李>

那么随机因子将永远不会改变那些零,并且模型可以通过检查示例中的四个零组来识别一个数字。

当然,这只是一种假设……模型可能还有许多其他可能的方法来解决随机因素……但如果存在,就足以表明模型可以找到它。

【讨论】:

  • 我不明白。所以我的嵌入总是为标签“0”创建相同的数组,array_0。在它创建 array_0 之后,我将噪声相乘。那么嵌入如何“为野生随机因素创造补偿”呢?因为在我乘以噪声 (noise*array_0=array_0_with_noise) 之后,每个 array_0_with_noise 都完全不同,不再接近 array_0。顺便说一句:我用完整的代码编辑了我的帖子。也许这有帮助。
  • 查看答案中添加的图片。如果我们能看到一个模式,模型也能看到一个模式。您的模型不是搜索某些特定值,而是搜索某些特定维度。
  • 例如,一种可能性是嵌入决定对于标签 8,潜在维度 (100) 中的前 10 个元素将始终为零。然后很容易通过检查前十个元素是否为零来识别八(零消除了相乘的随机因子)。其他 90 个元素可以随心所欲,其中许多元素为零的可能性很小。
  • 所以,如果我创建自己的嵌入数组。它们看起来像这样:array_0=[1,0,0,0,0,0,0,0,0,0, random_uniform(90, minval=-1.0,maxval=1.0], array_1=[0,1,0,0,0,0,0,0,0,0, random_uniform(90, minval=-1.0,maxval=1.0], array_3=[0,0,1,0,0,0,0,0,0,0, random_uniform(90, minval=-1.0,maxval=1.0],array_3 也一样,依此类推。那么代码应该是一样的,对吧?但它不是,它只是给我相同的图像
  • 或者我创建一个包含 90 个 0 和 10 个 1 的数组,应该可以吗?我试过了,但每个创建的 0 图像看起来都完全相同。与其他数字相同。但是当我使用 Keras 嵌入时,它可以工作。我无法理解。因为我的号码也被嵌入了,我乘以相同的噪音。
猜你喜欢
  • 2013-10-06
  • 2015-01-27
  • 1970-01-01
  • 2018-01-21
  • 1970-01-01
  • 2012-08-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多